详解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 相关文章推荐
深入聊聊Array的sort方法的使用技巧.详细点评protype.js中的sortBy方法
Apr 12 Javascript
提升你网站水平的jQuery插件集合推荐
Apr 19 Javascript
js实现跟随鼠标移动且带关闭功能的图片广告实例
Feb 26 Javascript
在JS中操作时间之getUTCMilliseconds()方法的使用
Jun 10 Javascript
动态生成的DOM不会触发onclick事件的原因及解决方法
Aug 06 Javascript
浅析JS中对函数function的理解(基础篇)
Oct 14 Javascript
vuejs2.0实现一个简单的分页示例
Feb 22 Javascript
对vue 键盘回车事件的实例讲解
Aug 25 Javascript
简述vue路由打开一个新的窗口的方法
Nov 29 Javascript
JS实现TITLE悬停长久显示效果完整示例
Feb 11 Javascript
vscode中的vue项目报错Property ‘xxx‘ does not exist on type ‘CombinedVueInstance<{ readyOnly...Vetur(2339)
Sep 11 Javascript
ant design vue中表格指定格式渲染方式
Oct 28 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
全国FM电台频率大全 - 21 海南省
2020/03/11 无线电
Laravel模糊查询区分大小写的实例
2019/09/29 PHP
浅谈laravel-admin的sortable和orderby使用问题
2019/10/03 PHP
laravel框架语言包拓展实现方法分析
2019/11/22 PHP
javascript 常用方法总结
2009/06/03 Javascript
运用jQuery定时器的原理实现banner图片切换
2014/10/22 Javascript
fckeditor粘贴Word时弹出窗口取消的方法
2014/10/30 Javascript
JavaScript中通过prototype属性共享属性和方法的技巧实例
2015/03/13 Javascript
使用JavaScript制作一个简单的计数器的方法
2015/07/07 Javascript
深入剖析JavaScript面向对象编程
2016/07/12 Javascript
js拼接html字符串的注意事项
2016/10/13 Javascript
小发现之浅谈location.search与location.hash的问题
2017/06/23 Javascript
vue实现验证码按钮倒计时功能
2018/04/10 Javascript
前端axios下载excel文件(二进制)的处理方法
2018/07/31 Javascript
浅谈目前可以使用ES10的5个新特性
2019/06/25 Javascript
vue router 跳转时打开新页面的示例方法
2019/07/28 Javascript
解决Layui中layer报错的问题
2019/09/03 Javascript
Node.js中console.log()输出彩色字体的方法示例
2019/12/01 Javascript
vue使用vant中的checkbox实现全选功能
2020/11/17 Vue.js
推荐下python/ironpython:从入门到精通
2007/10/02 Python
下载糗事百科的内容_python版
2008/12/07 Python
python实战之实现excel读取、统计、写入的示例讲解
2018/05/02 Python
Python3 串口接收与发送16进制数据包的实例
2019/06/12 Python
详解python和matlab的优势与区别
2019/06/28 Python
基于Django signals 信号作用及用法详解
2020/03/28 Python
Python3实现个位数字和十位数字对调, 其乘积不变
2020/05/03 Python
Django3中的自定义用户模型实例详解
2020/08/23 Python
"引用"与指针的区别是什么
2016/09/07 面试题
测绘专业大学生职业生涯规划书
2014/02/10 职场文书
致跳高运动员加油稿
2014/02/12 职场文书
社区党员志愿服务活动方案
2014/08/18 职场文书
党员对照检查材料思想汇报(党的群众路线)
2014/09/24 职场文书
英文版辞职信
2015/02/28 职场文书
餐饮店长岗位职责
2015/04/14 职场文书
2015年基层党支部工作总结
2015/05/21 职场文书
教你使用Jenkins集成Harbor自动发布镜像
2022/04/03 Servers