详解AngularJS脏检查机制及$timeout的妙用


Posted in Javascript onJune 19, 2017

||浏览器事件循环和Angular的MVW

“脏检查”是Angular中的核心机制之一,它是实现双向绑定、MVVM模式的重要基础。

Angular将双向绑定转换为一堆watch表达式,然后递归检查这些watch表达式的结果是否变了,如果变了,则执行相应的watcher函数。等到Model的值不再变化,也就不会再有watcher函数被触发,一个完整的digest循环就结束了。

因为我们不需要改变编程思维,就能用相同的语言、相同的事件模型,快速开发NodeJS程序,所以NodeJS迅速火起来,JavaScript full-stack也日渐流行。

我们经常听说Angular是一个MV*的框架,这是因为Angular拓展了浏览器的事件模型,建立了一个自己的上下文环境。

||Angular中的$watch函数

watch表达式很灵活:可以是一个函数,可以是$scope上的一个属性名,也可以是一个字符串形式的表达式。$scope上的属性名或表达式,最终仍会被$parse服务解析为响应的获取属性值的函数。

所有的watcher函数都会被unshift函数插入scope.$$watchers数组的头部,以便后边的$digest使用。

最后,$watch函数会返回一个反注册函数,一旦我们调用它,就可以移除刚才注册的watcher。

需要注意的是,Angular默认是不会使用angular.equals()函数进行深度比较的,因为使用===比较会更快,所以,它对数组或者Object进行比较时检查的是引用。这就导致内容完全相同的两个表达式被判定为不同。如果需要进行深度比较,第三个可选参数objectEquality,需要显式设置为true,如$watch('someExp', function(){...}, true)。

Angular还提供了$watchGroup、$watchCollection方法来监听数组或者是一组属性。

||Angular中的$digest函数

前面提到Angular拓展了浏览器的事件循环,这是怎么回事呢?

当接受View上的事件指令所转发的事件时,就会切换到Angular的上下文环境,来相应这类事件,$digest循环就会触发。

$digest循环实际上包括两个while循环。它们分别是:处理$evalAsync的异步运算队列,处理$watch的watchers队列。

当$digest循环发生的时候,它会遍历当前$scope及其所有子$scope上已注册的所有watchers函数。

遍历一遍所有watcher函数称为一轮脏检查。执行完一轮脏检查,如果任何一个watcher所监听的值改变过,那么就会重新再进行一轮脏检查,直到所有的watcher函数都报告其所监听的值不再变了。

当$digest循环结束时,才把模型的变化结果更新到DOM中去。这样可以合并多个更新,防止频繁的DOM属性。

需要注意的是,在$digest循环结束之前,如果超过了10轮脏检查,就会抛出一个异常,以防止脏检查无限循环下去。

什么时候会进入这个Angular的上下文环境,触发“脏检查机制”呢?这个问题很重要,它同时也是比较让人头疼的地方。

每一个进入Angular上下文环境的事件,都会执行一次$digest循环。对于ngModel监听的表单交互控件来说,每输入一个字符,就会触发一次循环来检查$watcher函数,以便及时更新View。在Angular1.3之后可以利用ngModelOptions进行配置,来修改默认的触发方式。

||Angular中的$apply

$digest是一个内部函数,正常的应用代码中是不应该直接调用它的。要想主动触发它,就要调用scope.$apply函数,它是触发Angular“脏检查机制”的常用公开接口。

需要注意的是:Angular只能管理它所已知的行为触发方式,而不能涵盖所有的Angular操作场景。这就为什么我们在封装第三方jQuery插件时,不能自动更新视图,而需要我们手动调用$scope.$apply。

集成jquery插件的时候,有时会出现digest in progress错误。如果排除Bug之后仍然不能解决,那么可以考虑用$timeout来解决。

$timeout的妙用

在延时任务中修改被绑定到界面中的变量,那么window.setTimeout是不会触发“脏检查”来更新UI界面的。你可能想:加上$scope.$apply不就解决了嘛。是的,这能解决UI界面更新的问题,但是你可能会遇到另一个问题:

 Error: $digest already in progress

这是怎么回事儿?哦,Angular内部正在进行“脏检查”。一位聪明的程序员巧妙地写了下面一段代码来解决这个问题:

function safeApply(scope, fn){ 
  (scope.
phase||scope.$root.
phase) ? fn() : scope.$apply(fn); 
}

代码中,在执行apply函数之前会首先检查Angular内部是不是正在做“脏检查”,如果是就直接执行函数,不用$apply;反之没有启动脏检查,那么就$apply执行该函数。呵呵,“完美”解决,不是吗?

请注意,笔者在上面的完美两个字上加了引号。Angular已经为我们内置了$timeout服务,它是Angular包装原生javascript window.setTimeout而实现的。

$timeout有很多妙用,但一定不要滥用,$timeout实现apply功能不应该是我们的第一方案,第一方案仍然应该是使用Angular内置的指令。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS 日期验证正则附asp日期格式化函数
Sep 11 Javascript
JQuery操作单选按钮以及复选按钮示例
Sep 23 Javascript
javascript闭包传参和事件的循环绑定示例探讨
Apr 17 Javascript
jQuery中on()方法用法实例详解
Feb 06 Javascript
JavaScript中的anchor()方法使用详解
Jun 08 Javascript
跟我学习javascript的prototype原型和原型链
Nov 18 Javascript
JS/jQ实现免费获取手机验证码倒计时效果
Jun 13 Javascript
PHP实现本地图片上传和验证功能
Feb 27 Javascript
JavaScript实现背景自动切换小案例
Sep 27 Javascript
Preload基础使用方法详解
Feb 03 Javascript
JavaScript中继承原理与用法实例入门
May 09 Javascript
详解React 条件渲染
Jul 08 Javascript
深入理解AngularJs-scope的脏检查(一)
Jun 19 #Javascript
jQuery 实现双击编辑表格功能
Jun 19 #jQuery
Web制作验证码功能实例代码
Jun 19 #Javascript
angularjs+bootstrap实现自定义分页的实例代码
Jun 19 #Javascript
详解vue服务端渲染(SSR)初探
Jun 19 #Javascript
jQuery实现简单的手风琴效果
Apr 17 #jQuery
原生JS+Canvas实现五子棋游戏实例
Jun 19 #Javascript
You might like
怎样在PHP中通过ADO调用Asscess数据库和COM程序
2006/10/09 PHP
PHP中使用unset销毁变量并内存释放问题
2012/07/05 PHP
php中判断数组相等的方法以及数组运算符介绍
2015/03/30 PHP
PHP合并discuz用户脚本的方法
2015/08/04 PHP
简单了解将WordPress中的工具栏移到底部的小技巧
2015/12/31 PHP
PHP实现的登录,注册及密码修改功能分析
2016/11/25 PHP
Yii框架where查询用法实例分析
2019/10/22 PHP
javaScript如何处理从java后台返回的list
2014/04/24 Javascript
Javascript获取表单名称(name)的方法
2015/04/02 Javascript
Bootstrap每天必学之导航条(二)
2016/03/01 Javascript
jquery实现图片放大点击切换
2017/06/06 jQuery
VueJs单页应用实现微信网页授权及微信分享功能示例
2017/07/26 Javascript
使用 electron 实现类似新版 QQ 的登录界面效果(阴影、背景动画、窗体3D翻转)
2018/10/23 Javascript
VUE2.0+ElementUI2.0表格el-table实现表头扩展el-tooltip
2018/11/30 Javascript
vue+Element实现搜索关键字高亮功能
2019/05/28 Javascript
vue从零实现一个消息通知组件的方法详解
2020/03/16 Javascript
javascript实现前端成语点击验证优化
2020/06/24 Javascript
[06:10]6.81新信使新套装!给你一个炫酷的DOTA2
2014/05/06 DOTA
python安装教程 Pycharm安装详细教程
2017/05/02 Python
python+tkinter编写电脑桌面放大镜程序实例代码
2018/01/16 Python
Python爬虫动态ip代理防止被封的方法
2019/07/07 Python
python打造爬虫代理池过程解析
2019/08/15 Python
python 并发编程 多路复用IO模型详解
2019/08/20 Python
浅谈sklearn中predict与predict_proba区别
2020/06/28 Python
Python faker生成器生成虚拟数据代码实例
2020/07/20 Python
Reebonz中国官网:新加坡奢侈品购物网站
2017/03/17 全球购物
美国智能家居专家:tink
2019/06/04 全球购物
Athleta官网:购买女士瑜伽服、技术运动服和休闲运动服
2020/11/12 全球购物
小学教师自我鉴定范文
2014/03/20 职场文书
企业新年寄语
2014/04/04 职场文书
消防安全承诺书
2014/05/22 职场文书
工商管理专业自荐信
2014/06/03 职场文书
农村环境卫生倡议书
2015/04/29 职场文书
男方家长婚礼致辞
2015/07/27 职场文书
MySQL kill不掉线程的原因
2021/05/07 MySQL
JUnit5常用注解的使用
2021/07/02 Java/Android