当前位置: 首页 > 编程笔记 >

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

窦涵忍
2023-03-14
本文向大家介绍详解AngularJS脏检查机制及$timeout的妙用,包括了详解AngularJS脏检查机制及$timeout的妙用的使用技巧和注意事项,需要的朋友参考一下

||浏览器事件循环和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内置的指令。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍AngularJS的脏检查深入分析,包括了AngularJS的脏检查深入分析的使用技巧和注意事项,需要的朋友参考一下 写在开头 关于Angular脏检查,之前没有仔细学习,只是旁听道说,Angular 会定时的进行周期性数据检查,将前台和后台数据进行比较,所以非常损耗性能。 这是大错而特错的。我甚至在新浪前端面试的时候胡说一通,现在想来真是羞愧难当! 没有深入了解就信口开河实在难堪大

  • 本文向大家介绍妙解Java中的回调机制(CallBack),包括了妙解Java中的回调机制(CallBack)的使用技巧和注意事项,需要的朋友参考一下 前言 最近学习java,接触到了回调机制(CallBack)。初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义。当然了,我在理解了回调之后,再去看网上的各种讲解,确实没什么问题。但

  • 本文向大家介绍详解AngularJS中的依赖注入机制,包括了详解AngularJS中的依赖注入机制的使用技巧和注意事项,需要的朋友参考一下  依赖注入是一个在组件中给出的替代了硬的组件内的编码它们的依赖关系的软件设计模式。这减轻一个组成部分,从定位的依赖,依赖配置。这有助于使组件可重用,维护和测试。 AngularJS提供了一个至高无上的依赖注入机制。它提供了一个可注入彼此依赖下列核心组件。   

  • 本文向大家介绍AngularJS控制器详解及示例代码,包括了AngularJS控制器详解及示例代码的使用技巧和注意事项,需要的朋友参考一下 AngularJS应用主要依赖于控制器来控制数据在应用程序中的流动。控制器采用ng-controller指令定义。控制器是一个包含属性/属性和JavaScript对象的功能。每个控制器接受$scope参数指定应用程序/模块,由控制器控制。 在这里,我们已经声明

  • 本文向大家介绍node.js超时timeout详解,包括了node.js超时timeout详解的使用技巧和注意事项,需要的朋友参考一下 如果在指定的时间内服务器没有做出响应(可能是网络间连接出现问题,也可能是因为服务器故障或网络防火墙阻止了客户端与服务器的连接),则响应超时,同时触发http.ServerResponse对象的timeout事件. response.setTimeout(time,

  • 本文向大家介绍详解python eval函数的妙用,包括了详解python eval函数的妙用的使用技巧和注意事项,需要的朋友参考一下 python eval函数功能:将字符串str当成有效的表达式来求值并返回计算结果。 函数定义: 将字符串str当成有效的表达式来求值并返回计算结果。globals和locals参数是可选的,如果提供了globals参数,那么它必须是dictionary类型;如果

  • 本文向大家介绍详解AngularJS控制器的使用,包括了详解AngularJS控制器的使用的使用技巧和注意事项,需要的朋友参考一下 控制器在Angularjs中的作用是增强视图,它实际就是一个函数,用来向视图中的作用域添加额外的功能,我们用它来给作用域对象设置初始状态,并添加自定义行为。 当我们在页面上创建一个控制器时,Angularjs会生成并传递一个$scope给这个控制器,由于Angular

  • 问题内容: 在“旧的JDBC美好时光”中,我编写了许多SQL代码,这些代码仅针对实际上已更改的“属性/成员”进行了针对性的更新: 例如,考虑具有以下成员的对象: 如果仅在某些业务方法中进行了更改,我将只为该成员发出一个SQL 。 但是,似乎(这是我对Hibernate的“印象”)在使用标准Hibernate映射(映射完整类)时,即使仅单个成员的更新也会导致Hibernate生成的SQL语句中对象的