angularJS $watch $apply $digest
看O'Reilly的书看到$watch这部分,不过没看懂,网上很多资料也含糊不清,不过还是找到了几个好的,简单记录一下。
一句话说明,$watch是用来监视变量的,好了直接上代码
<html>
<head>
<script src='./js/angular.min.js'></script>
</head>
<body ng-app='watch'>
<input ng-model='name' type='text'/>
<div>change count: {{count}}</div>
<script>
var app = angular.module('watch',[]);
app.run(['$rootScope',function($rootScope){
$rootScope.count = 0;
$rootScope.name = 'Alfred';
$rootScope.$watch('name',function(){
$rootScope.count++;
})
}]);
</script>
</body>
</html>
上面这个代码就是用来监控name的变化的,每次当我们在input输入框中输入一个值的时候,$rootScope中的count就会对应的+1。
在angularJS的内部,每当我们对name的值进行修改的时候,angularJS内部中的$digest就会被调用一次,并在运行结束之后检查我们用$watch来监控的模型,如何和上一次执行$digest之前相比发生变化了,则执行$watch中的回调函数。
然而!!!在我们实际的开发中,仅仅实现对一个原始类型的数据监控是远远不能够满足所需的,对于原始类型的数据,如果我们使用了一次赋值操作,则这个原始类型的数据变量会真正的赋值一次,然而对于引用类型的变量,进行赋值时,仅仅是将赋值的变量指向了这个引用类型。
<html>
<head>
<script src='./js/angular.min.js'></script>
</head>
<body ng-app='watch'>
<div ng-repeat='item in items'>
<input ng-model='item.a'/><span>{{item.a}}</span>
</div>
<div>change count: {{count}}</div>
<script>
var app = angular.module('watch',[])
app.run(['$rootScope',function($rootScope){
$rootScope.count = 0;
$rootScope.items = [
{ "a": 1 },
{ "a": 2 },
{ "a": 3 },
{ "a": 4 }
];
$rootScope.$watch('items',function(){
$rootScope.count++;
});
}]);
</script>
</body>
</html>
在这个栗子中我们就会发现,不管我们怎么改变其中的值,count都不会发生变化的。而这个就是我们上面说那样,在说明这个之前,我们在说明下$watch的第三个参数,一般$watch函数的前两个参数是必传的(监控对象,回调函数),第三个参数默认为false,这样的话我们进行的监控叫做引用监控,这个意思就是监控对象的应用没有发生变化的时候就不算对象发生了变化,具体的来说,上面的例子,就算items的属性发生了变化,只要items的引用没有发生变化,$watch就都当做没有看见,但是比如讲一个数组赋值给items时,这个时候$watch就看不下去了。
相反,如果第三个参数设置为true的时候,那么我们的监控叫做“全等监控”,此时的$watch的要求就是比较苛刻了,只要他监控的对象有一点点变化时,$watch就会跳出来,卧槽!你居然还敢动!!!
当然值得提一下的是:为什么第三个参数加个true,这么方便了我们还不加呢?!当然是牵涉到性能的问题啦!全等监控运行起来的时候是先监控到整个对象,然后在每一次把$digest跑起来之前先用angualr.copy()将整个对象先拷贝之后再调用angular.equal()方法来进行比较,所以这一监控可能会消耗大量的资源!
在angularJS 1.1.4又出来了一个$watchCollection()方法,专门来监控数组集合的,他的性能介于引用监控和全等监控之间,它不会对数组的每一项内容 进行监控,而是当数组的pop和push时候做出反应。
$apply
$apply的作用是把改变同步绑定到界面上,但是它为什么存在呢?什么时候需要用它呢?什么时候又不需要呢?
那么我们首先说一下angular是如何进行数据双向绑定的吧。
要知道一个变量变了,方法无非就两种
1、 通过固定的接口,比如set,get方法,通过set设置变量的值,set被调用时做个比较就可以,但是这个方法和复杂!
2、 脏检查,将某一个对象复制一份快照,在某个时间,比较现在对象与快照的值。很明显,这个方法要复制两份对象,而且要遍历对象,比较每一个属性。对!这样的确有性能问题!但是angular就是用这个的~
但是人家angular的脏检查不是对所有对象进行检查,只是当对象绑定到html中,该对象才复合检查对象(watcher),同理,angular对属性的脏检查也是如此。
watcher源码
watcher = {
fn: listener, //监听回调函数
last: initWatchVal, //上一状态值
get: get, //取得监听的值
exp: watchExp, //监听表达式
eq: !!objectEquality //要不要比较引用
};
那么我们什么时候去进行脏检查呢?
脏检查的点是在函数执行完之后,但是不标明异步调用也执行完毕,如果我们的功能是异步的,那么我们会发现我们的改变并没有更新到DOM上。
举个栗子:
function Ctrl($scope) {
$scope.message = "Waiting 2000ms for update";
setTimeout(function () {
$scope.message = "Timeout called!"; // AngularJS unaware of update to $scope }, 2000);
},2000);
}
DOM上永远都不会显示Timeout called
当然,这个就是我们$apply的应用场景了,调用它,手动触发脏检查,举个例子:angularJs提供了$timeout,为什么咱有了setTimeout还要提供这个呢?就是应为$timeout异步完成后,angularJs会自动触发$apply。
注:应该尽可能地把要执行的代码和函数传递给$apply去执行,而不要自已执行那些函数然后再调用$apply。
例如
$scope.$apply(function() {
$scope.variable1 = 'some value';
executeSomeAction();
});
尽可能别这样
$scope.variable1 = 'some value';
executeSomeAction();
$scope.$apply();
$digest
然而这个脏检查有事怎么检查的呢??
$apply被调用后最终都会触发$digest()
在调用了$scope.$digest()后,$digest循环就开始了。
假设你在一个ng-click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地通过调用$digest()来触发一轮$digest循环。
当$digest循环开始后,它会触发每个watcher。这些watchers会检查scope中的当前model值是否和上一次计算得到的model值不同。
如果不同,那么对应的回调函数会执行。
调用该函数的结果,就是view中的表达式内容会被更新。
除了ng-click指令,还有一些其它的built-in指令以及服务来让你更改models(比如ng-model,$timeout 等 ) 和自动触发一次 $digest 循环。
$apply补充
HTML:
<body ng-app="myApp">
<div ng-controller="MessageController">
Delayed Message: {{message}}
</div>
</body>
JavaScript:
angular.module('myApp',[]).controller('MessageController', function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.message = 'Fetched after 2 seconds';
console.log('message:'+$scope.message);
}, 2000);
}
$scope.getMessage();
});
通过运行这个例子,你会看到过了两秒钟之后,控制台确实会显示出已经更新的model,然而,view并没有更新。原因也许你已经知道了,就是我们忘了调用$apply()方法。因此,我们需要修改getMessage(),如下所示:
angular.module('myApp',[]).controller('MessageController', function($scope) {
$scope.getMessage = function() {
setTimeout(function() {
$scope.$apply(function() {
//wrapped this within $apply
$scope.message = 'Fetched after 3 seconds';
console.log('message:' + $scope.message);
});
}, 2000);
}
$scope.getMessage();
});
如果你运行了上面的例子,你会看到view在两秒钟之后也会更新。唯一的变化是我们的代码现在被wrapped到了$scope.$apply()中,它会自动触发$rootScope.$digest(),从而让watchers被触发用以更新view。
Note:顺便提一下,你应该使用$timeout service来代替setTimeout(),因为前者会帮你调用$apply(),让你不需要手动地调用它。
而且,注意在以上的代码中你也可以在修改了model之后手动调用没有参数的$apply(),就像下面这样:
$scope.getMessage = function() {
setTimeout(function() {
$scope.message = 'Fetched after two seconds';
console.log('message:' + $scope.message);
$scope.$apply(); //this triggers a $digest
}, 2000);
};
以上的代码使用了$apply()的第二种形式,也就是没有参数的形式。需要记住的是你总是应该使用接受一个function作为参数的$apply()方法。这是因为当你传入一个function到$apply()中的时候,这个function会被包装到一个try…catch块中,所以一旦有异常发生,该异常会被$exceptionHandler service处理。
angularJS $watch $apply $digest的更多相关文章
- AngularJS中的digest循环$apply
欢迎大家指导与讨论 : ) 前言 Angular会拓展这个标准的浏览器流程,创建一个Angular上下文.这个Angular上下文指的是运行在Angular事件循环内的特定代码,该Angular事件循 ...
- $watch、$digest、$apply
$watch.$digest.$apply $watch 代表的就是对数据源的监听,当数据源发生变化,就会触发第二个参数的回调函数 $digest 代表触发一个数据源变化的事件 $apply 代表对于 ...
- angularJS $watch $digest $apply
一 简介AngularJS提供了一个非常酷的特性叫做双向数据绑定(Two-way Data Binding),这个特性大大简化了我们的代码编写方式.数据绑定意味着当View中有任何数据发生了变化,那么 ...
- angular的$watch,$digest和$apply
第一部分:$watch $watch是一个scope函数,用于监听模型变化,当你的模型部分发生变化时它会通知你. $watch(watchExpression, listener, objectEqu ...
- Client-Side Template Injection with AngularJS
<html> <head> <meta charset="utf-8"> <script src="https://cdn.bo ...
- $digest / $apply digest in progress报错
有的时候出于某种原因,如jq操作了model.或者$watch.setTimeout等函数改变了model,导致最后没有脏数据检测.所以我没就手动调用了$apply( )等.但是第一次运行的时候ang ...
- Atitit HTTP 认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结
Atitit HTTP认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结 1.1. 最广泛使用的是基本验证 ( ...
- (转)构建自己的AngularJS,第一部分:Scope和Digest
原翻译链接:https://github.com/xufei/Make-Your-Own-AngularJS/edit/master/01.md 原文链接:http://teropa.info/blo ...
- 理解Angular中的$apply()以及$digest()
$apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的 ...
- 指令<AngularJs>
对于指令,可以把它简单的理解成在特定DOM元素上运行的函数,指令可以扩展这个元素的功能. 首先来看个完整的参数示例再来详细的介绍各个参数的作用及用法: angular.module('myApp', ...
随机推荐
- jquery ajax 跨域问题
前言:关于跨域CORS 1.没有跨域时,ajax默认是带cookie的 2.跨域时,两种解决方案: 1)服务器端在filter中配置 详情:http://blog.csdn.net/wzl002/ar ...
- Head First Java & final
- 学术诚信与职业道德——《构建之法》P384~391读后感
程序本身没有伦理和职业道德, 但是程序员和软件企业要有,因为程序员明白伦理道德的存在. 对于刚刚经历被不负责队友抛下的经历,对此很有感触,软件工程师除了遵守任务做事,也要考虑道德上.责任上的事情. 就 ...
- 软工网络15团队作业8——Beta阶段敏捷冲刺(用户使用调查报告)
一.项目概述 1.项目名称 考研必背 2.项目简介 微信小程序,帮助考研学生记忆单词. 3.项目预期达到目标 用户无需下载app,仅通过微信小程序就可以达到背单词的目的,并且能够制定背单词的计划. 4 ...
- web安全测试系统
最近写了个简单的web安全实践系统部署到了docker中 下载方式:docker pull ju5ton1y/websecurity github Dockerfile下载地址:https://git ...
- [转帖] 固定硬盘接口 U.2和M.2
U.2接口 U.2接口别称SFF-8639,是由固态硬盘形态工作组织(SSD Form Factor Work Group)推出的接口规范.U.2不但能支持SATA-Express规范,还能兼容SAS ...
- (转)web性能优化
前端是庞大的,包括 HTML. CSS. Javascript.Image .Flash等等各种各样的资源.前端优化是复杂的,针对方方面面的资源都有不同的方式.那么,前端优化的目的是什么 ? 1. 从 ...
- vue 组件 模板中根数据绑定需要指明路径并通信父
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>T ...
- 重新认识javascript的settimeout和异步
1.简单的settimeout setTimeout(function () { while (true) { } }, 1000); setTimeout(function () { alert(' ...
- 自定义smokeping告警(邮件+短信)
前段时间接到公司IT同事需求,帮助其配置smokeping的告警功能,之前配置的姿势有些问题,告警有些问题,现在调试OK,在此将关键配置点简单记录下. 关键的配置项主要有: 定义告警规则并配置将告警信 ...