angular核心$watch,$digest,$apply之间的联系
浏览器事件发生时,会在浏览器的上下文window中执行,而angular有自己的上下文angular content,angular 事件在自己的上下文angular content中执行。
$watch
$watch组成了双向绑定的一边,在指定的表达式改变时调用,每个作用域都维护了一个监视器列表,被称为$scope.$$watchers。每次你绑定一些东西到你的UI上时你就会往$watch队列里插入一条$watch。
<ul>
<li ng-repeat="person in people">
{{person.name}} - {{person.age}}
</li>
</ul> app.controller('MainCtrl', function($scope) {
$scope.people = [
{name: 'a', age: 21},
{name: 'b', age: 25},
{name: 'c', age: 22}
];
});
这个例子生成了多少个$watch呢?ng-repeat一个,每个person两个,一共(2*3)+1=7个。
除了在视图上绑定数据创建一个$watch,还可以自定义$watch,但是有几点需要注意。
<body ng-controller="MainCtrl">
<input ng-model="user.name" />
Name updated: {{updated}} times.
</body> app.controller('MainCtrl', function($scope) {
$scope.user = { name: "Fox" }; $scope.updated = 0; $scope.$watch('user', function(newValue, oldValue) {
if (newValue === oldValue) { return; } //防止初始化时调用
$scope.updated++;
}, true); //第三个参数可选,如果设为true,则会比较对象的值,否则比较引用
});
当controller执行到这个$watch时,它会立即执行一次,所以我们加入了判断来防止第一个初始化的调用。其次是第三个参数,如果省略或者为false,则以上例子输入框的值改变之后,update的值不会改变,因为比较的user的引用。
每一个绑定到了UI上的数据都会生成一个$watch,那这些$watch是什么时候生成的呢?当我们的模版加载完毕时,也就是在linking阶段(Angular分为compile阶段和linking阶段),Angular解释器会寻找每个directive,然后生成每个需要的$watch。
$digest
var dirty = true;
var iterations = 0;
while (dirty && iterations++ < TIMES_TO_LOOP) {
dirty = false;
for (var i = 0; i < scope.watchers.length(); i++) {
var currentValue = scope.watchers[i].get();
if (currentValue != scope.watchers[i].oldValue) {
dirty = ture; //如果有数据改变了,则又$digest一次,直到没有数据改变
scope.watchers[i].callback(currentValue, scope.watchers[i].oldValue);
scope.watchers[i].oldValue = currentValue;
}
}
}
angular将TIMES_TO_LOOP(TTL)默认设为10,即$digest最多循环10次,超出后将抛出一个错误,防止无限循环,我们可以通过$rootScope服务和digestTtl函数改变这个值。
var app = angular.module('MyApp', [], function ($rootScopeProvider) {
$rootScopeProvider.digestTtl(15); //将TTL改为15
});
angular的内置指令会默认调用$digest,还有一点需要注意的是,UI中的所有指令都将创建至少一个监视器,所以angular的性能问题几乎总是因为以不合理的方式使用ngRepeat指令所引起的。如以上第一段代码,如果person对象达到10000个以上就会严重影响性能,实际上angular建议单个页面中不要使用超过2000个监视器。
angular.module('app', function ($scope) {
setTimeout(function () {
$scope.arr = [1];
$scope.$digest(); //setTimeout中手动调用$digest
});
});
$apply
function $apply(expr) {
try {
return$eval(expr);
} catch(e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
事件发生时是谁在决定事件是在window中执行还是angular content执行?答案是$apply(),例如内置的ngClick指令会默认调用$apply。那么什么时候需要我们手动调用$apply呢?和$digest一样,在angular里面执行angular框架之外的表达式,比如说:浏览器DOM事件、setTimeout、XHR或其他第三方的库时,这时就要求我们手动调用$apply()。
element.bind('click', function() {
scope.foo++;
scope.bar++;
scope.$apply(); //我们是在angular context的外面更新的数据,
//如果有发生错误,Angular永远不知道
});
element.bind('click', function() {
scope.$apply(function() { //把它封装进$apply里面,错误会传到angular里
scope.foo++; //例如一个服务器获取数据失败
scope.bar++;
});
})
angular核心$watch,$digest,$apply之间的联系的更多相关文章
- 谈谈Angular关于$watch,$apply 以及 $digest的工作原理
这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的, 如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了. angularjs使 ...
- Angular:手动脏检查/$apply/$digest和监控对象/$watch
声明:借鉴好多chm资料.视频.PDF总结如下: 一.$apply的引入 View <div ng-app=""> <div ng-controller=&quo ...
- angular : direative :comunication 指令之间的通讯
在网络上可以找到多种指令之间的通讯 · $on,$emit,$boardcast (向上或向下冒泡) · 指令return的required (^)向上一个scope通讯,前提要先给scope一个na ...
- Angular学习笔记之组件之间的交互
1.@Input:可设置属性 当它通过属性绑定的形式被绑定时,值会“流入”这个属性. 在子组件中使用,例如:@Input()name:string 父组件定义宾亮,并在父组件的模板中绑定,例如: 子组 ...
- angular中对象与字符串之间的转换
1.angular 里 字符串与对象互转 angular.toJson();将字符串转成对象 angular.forJson(); 将字符串转成对象 2.angular 循环 <scr ...
- Angular 学习笔记——自定义指令之间的交互
<!DOCTYPE html> <html lang="en" ng-app="myApp"> <head> <met ...
- Vue与Angular以及React的三者之间的区别
1.与AngularJS的区别 相同点:都支持指令:内置指令和自定义指令:都支持过滤器:内置过滤器和自定义过滤器:都支持双向数据绑定:都不支持低端浏览器. 不同点:AngularJS的学习成本高,比如 ...
- Angular DirtyChecking(脏值检查) $watch, $apply, $digest
Dirty Checking (脏值检查) Digest cycle and $scope Digest cycle and $scope First and foremost, AngularJS ...
- angular $apply()以及$digest()讲解1
一些知名的批评和缺陷.他们都涉及到$digest loop(更新周期)中一个很常见的问题:如何在Angular之外更新$scope? 在哪调用 $apply? 更佳的做法是确保你是在$digest l ...
随机推荐
- JDBC数据库之添加数据
通过JDBC向数据库中添加数据,可以使用INSERT语句实现插入数据SQL语句,对于SQL语句中的参数可以只用占位符"?"代替,然后通过PreparedStatement对其赋值以 ...
- 只用一招让你Maven依赖下载速度快如闪电
一.背景 众所周知,Maven对于依赖的管理让我们程序员感觉爽的不要不要的,但是由于这货是国外出的,所以在我们从中央仓库下载依赖的时候,速度如蜗牛一般,让人不能忍,并且这也是大多数程序员都会遇到的问题 ...
- Apache Spark 2.2.0 中文文档 - Spark 编程指南 | ApacheCN
Spark 编程指南 概述 Spark 依赖 初始化 Spark 使用 Shell 弹性分布式数据集 (RDDs) 并行集合 外部 Datasets(数据集) RDD 操作 基础 传递 Functio ...
- Mybatis——choose, when, otherwise可以达到switch case效果
在mapping文件中实现动态sql,如果想达到if else的效果可以使用:choose, when, otherwise <choose> <when test="ti ...
- 教育,创新,提升:Indiegogo和Kickstarter上受中国用户支持的10个众筹项目
中国的经济正在迅速发展,已成为世界第二大经济体.中国家庭随着经济水平的提高,越来越多父母愿意将自己的子女送到海外留学. 家长们希望自己的子女可以有机会接受国外大学优质的教育, 以便他们将来可以学成归来 ...
- ptyhon 编程基础之函数篇(二)-----返回函数,自定义排序函数,闭包,匿名函数
一.自定义排序函数 在Python中可以使用内置函数sorted(list)进行排序: 结果如下图所示: 但sorted也是一个高阶函数,可以接受两个参数来实现自定义排序函数,第一个参数为要排序的集合 ...
- Rundeck部署和基本使用
rundeck 介绍 Rundeck 是一款能在数据中心或云环境中的日常业务中使程序自动化的开源软件.Rundeck 提供了大量功能,可以减轻耗时繁重的体力劳动.团队可以相互协作,分享如何过程自动化, ...
- Ubuntu16.04下安装redis
Ubuntu16.04下安装redis 保证网络畅通,选定好下载工作路径,执行以下命令下载redis-3.2.6: sudo wget http://download.redis.io/release ...
- 关于xamarin.forms 中 list 的loadmore
前言 最近几天在研究上拉加载啊,下拉刷新啊什么的.然而坑爹的事情总是那么多.在xamarin.forms中,list自带的,并没有上拉加载的这个属性(难道当初他们封装方法时,就不会想到数据多了会咋整吗 ...
- mysql error 1130 hy000:Host 'localhost' is not allowed to connect to this mysql server 解决方案
ERROR 1130 (HY000): Host 'localhost' is not allowed to connect to this MySQL server D:\Wamp\mysql-\b ...