标签:
AngularJS是一个很容易让人上瘾且深陷其中的JS框架,它的MVC特性大大减少的Web开发难度,提高代码后期的维护性。学会了这个框架,有时候甚至开发个最简单的静态页,你也会情不自禁问问自己,可以用Angular吗?
但事物都是有双面性的,简单说说那些AngularJS开发的。
AngularJS,核心模块加上route、cookies两个核心模块,压缩后min版本的体积大约120k,如果算上HTTP GZIP传输压缩的话,体积能缩减到60~70k左右那AngularJS的JS库体积真的大吗?对比想想jQuer框架压缩后也有100k,即使是同样的前端MVC、号称是精简到极致的Backbone,在加入几个依赖后的框架zepto or jQuery、underscore以后大小也达到了60~70k。这样想想,AngularJS真的不算大?
这个问题在PC端是肯定的,但别忘了,我们还有千千万万的2G网络移动端的用户。也许目前在移动端广泛使用的框架,依然是以zepto为代表的轻量级jQuery框架,但在未来,AngularJS这个单页应用非常适合做WebApp的框架真的为移动端Web带来更多更出色的体验。
与后端静态化页面(后端MVC框架)理念不一样,像.net的Razor、java的freemarker,后端处理好动态数据,并生成静态页面,浏览器接收资源仅负责渲染。
而AngularJS(前端MVC),浏览器接收到的仅是没处理HTML(可以理解就是模板),一边渲染,一边加载Angular.js文件;然后Angular初始化,设定事件
监听,并进入HTML结构找寻到带有{{ xxx }}、ng-xxx的标识,绑定动态数据,浏览器重新渲染。
因此,不可避免的是,前端MVC框架在初始化加载时,耗时成本会更高,影响用户体验。
Angular双向数据绑定、作用域、指令的性能瓶颈是我们在日常开发过程中要谨慎处理的。了解了其实现原理,也明白,这块的性能瓶颈是不可避免。
举一个最简单的例子,作用域下name对象,输入框修改name对象值,h1同步刷新{{name}}。
<!DOCTYPE html>
<html ng-app>
<head>
<title>Simple app</title>
</head>
<body>
<input ng-model="name" type="text" placeholder="Your name">
<h1>Hello {{ name }}</h1>
<script src="http://cdn.bootcss.com/angular.js/1.2.19/angular.js"></script>
</body>
</html>
代码很简单,一句JS代码都没有,就完成了将输入框输入值同步显示的效果。Angular如何实现?首先,Angular在初始化阶段时,做了两件事情:
1)、注册监听所有会改变作用域的事件,可以是指令(内置及自定义指令)、Form输入事件、地址变更、Ajax等等动作。如上例子就是,text input的键入事件;
看到on function是不是有种似曾相识的感觉,是的,就是类似jQuery的on绑定函数。
2)、创建作用域,建立DOM与作用域的映射,并在作用域Scope对象中创建$watch监听属性,$watch中会记录Scope最近两个值。
当触发监听事件时,Angular会带着域中新修改的对象值(脏值),进入到脏值循环的函数中$digest(),脏值循环函数会从根作用域$rootScope开始,
类似二叉树的运作模式,遍历根的所有子节点作用域$scope,取出每个作用域scope的watch最近两个值,然后比较是否发生变化,变化的话就重绘浏览器DOM。
显而易见,每次的赋值改变都会引发脏值循环,脏值循环会引起整个树状结构的刷新,不可避免产生一定性能问题。但是,Google那帮大神毕竟是大神啊,整个树干刷新这种蠢事他们是不会干的。
脏值循环是,从哪个节点作用域引发的改变就从哪个节点终止。如下图,假定D节点绑定的值发生改变,脏值循环会进入A B E C D,但是F不会。当然,如果是A发生改变,整个树干都会刷新。
结论是:值域绑定数量,就是整个树干的所有级别的所有节点数量应该控制在2000以下。我想,应该足够了吧。
参考以下例子Test1,应该能够说明问题,当$scope.lis循环值达到2000时,改变number值,整个页面会出现明显卡顿。
<!DOCTYPE html>
<html ng-app="newApp">
<head>
<!--<meta name=‘viewport‘ content=‘width=device-width,minimum-scale=1.0 maximum-scale=1.0 user-scalable=no‘>-->
<title>Simple app</title>
<style>
body {
font-size: 30px;
}
ul {
margin: 0;
}
.error {
background-color: red;
}
.level {
padding: 20px;
}
.level.level-root {
background-color: #f2f2f2;
}
.level.level-1 {
background-color: #c7e2f1;
}
.level.level-2 {
background-color: #edc79e;
}
</style>
</head>
<body>
<div ng-controller="FirstController" class="level level-root">
<div class="level level-1">
{{number}}
<input ng-model="number" value="{{number}}" />
<button type="button" ng-click="print()">print</button>
<ul ng-repeat="n in lis" class="level level-2">
<li>
<div>
{{number}}/{{n}}
<input ng-model="number" value="{{number}}" />
<button type="button" ng-click="print()">print</button>
<button type="button" ng-click="change(n)">change</button>
</div>
</li>
</ul>
</div>
</div>
<script src="http://cdn.bootcss.com/angular.js/1.2.19/angular.js"></script>
<script>
var app = angular.module(‘newApp‘, []);
app.controller(‘FirstController‘, ["$scope", "$http", function ($scope, $http) {
$scope.number = 1;
$scope.lis = [];
for (var i = 1; i <= 2000; i++) {
$scope.lis.push(i);
}
$scope.print = function () {
console.log($scope.number);
};
$scope.change = function (n) {
n = 5;
};
}]);
</script>
</body>
</html>
ps,有个要注意地方是:循环绑定值域只能向子级传递,不能逆向父级传递。
如下图节点2修改number值为1234,number不能向上父级传递,导致全局number错乱。
angular.js是核心模块,但不支持移动触摸应用,ng-click依然采用原生click事件,会有200ms延迟。注入angular-touch模块,在移动应用中,angular会自动将ng-click指令的click事件转为tap,并扩展swipe滑动支持。
但之所以说这块还不成熟是因为,目前很多增强触摸滑动体验的js库(iscroll、islider……),都是以拆分滑动事件区分处理为设计准则的。最简单的例子,向左滑动:islider会区分处理触摸前触碰瞬间的事件touchstart、拖动手指的事件touchmove、手指离开屏幕的事件touchend;然而,angular touch就给你提供一个动作,就是向左滑动,然后,没有然后了。
虽然说,各自运作问题不大,但如果要在angular项目中加入这些js库,到了数据绑定、DOM处理这些环节的时候,会出现很多不可预知的bug。
标签:
原文地址:http://www.cnblogs.com/tgor/p/4725615.html