AngularJS的digest循环和$apply

2019-08-15 10:16:22 浏览数 (1)

最近在写AngularJS,遇到一个问题,在Ajax异步请求后台数据,然后将结果赋值给当前scope中某对象的属性,在页面中怎么都取不到,然而在js端却可以正常打印出来。

分析原因:第一感觉是前端页面绑定指令不对,导致不能正常显示,然而变化各种指令都不能正常获取,很是郁闷;最后去掉Ajax,直接返回给页面,结果却是可以的,初步排除了与绑定指令相关。那问题出现在scope上了???结果查阅资料,终于得知,使用第三方框架(比如jQuery),或者调用setTimeout(),会导致其运行在AngularJS上下文外部,可以使用apply()函数让Angular返回apply()函数让Angular返回digest循环,传递到Angular应用中。

一、传统事件触发

在标准的浏览器流程中,页面加载、$http请求返回响应、鼠标移动以及按钮被点击等情况都会触发事件。当事件被触发时(比如点击一个链接),JavaScript会创建一个事件对象,并执行这个事件对象所在的监听特定事件的所有函数。然后浏览器会执行注册给该事件的回调函数,更新DOM。

注意:同一时间不能运行两个事件。

当使用angular时,其会扩展这个标准的浏览器流程,创建一个angular上下文(angular事件循环内的特定代码,该angular事件循环通常被称为$digest循环)。

二、$digest循环

digest循环有两个主要部分组成:digest循环有两个主要部分组成:watch列表,$evalAsync列表。

1. $watch列表

angular跟踪变化,是通过给watch列表添加一个监控函数做到的,需要注意的是所有绑定给同一watch列表添加一个监控函数做到的,需要注意的是所有绑定给同一scope对象的UI元素,只会添加一个watch到watch到watch列表中。这些watch列表会在watch列表会在digest循环中的“脏值检查”(检测值是否发生了变化,但整个应用还没有同步该变化)的程序解析。 (1)$watch(watchExpression, listener/callback, objectEquality)

(2)$watchCollection(obj/string, listener)

代码语言:javascript复制
$scope.$watchCollection('names', function(newNames, oldNames, scope) {
    // 发送变化的处理
});

2. $evalAsync列表

$evalAsync()方法是一种在当前作用域上调度表达式在未来某个时刻运行的方式。

指令、控制器调用$evalAsync(),会在angular操作DOM之后,浏览器渲染之前运行。所以,永远不要使用其来约定事件的顺序。

三、页面中的$digest循环

代码语言:javascript复制
<input type="text" ng-model="user.name" ng-minlength="3" />

(1)angular会设置一个隐式的监控器,将输入字段的值绑定为当前的$scope对象;

(2)当用户输入字符,angular上下文就会生效并开始遍历$$watchers($watch列表);

(3)监控函数在$scope.user.name绑定上执行;

(4)退出$digest循环之前,会触发该值(ng-model)上运行的验证和格式化操作;

(5)由于在digest循环中值发生了变化,angular需要再次运行这一循环以确定它没有改变作用域对象上的其他值。(原因:如果有一个名为scope.user.fullName的属性由scope.user.fullName的属性由scope.user.firstName和$scope.user.lastName组成,那么这两个值的变化多会引起fullName的变化,因此需要再次确认);

(6)$digest循环退出,浏览器重绘DOM以刷新视图。

四、$apply从外部进入上下文

所有指令ng-[event]指令(如ng-click)都会调用scope.scope.apply(),以强制运行$digest循环。

apply()函数可以从angular框架的外部让表达式在angular上下文内部执行。当手动处理事件,使用第三框架(比如jquery)或者调用setTimeout都可以使用apply()函数可以从angular框架的外部让表达式在angular上下文内部执行。当手动处理事件,使用第三框架(比如jquery)或者调用setTimeout都可以使用apply()函数将值传递到angular应用中。

(1)不建议在控制器中使用$apply(),因为这样会导致难以测试。 (2)jquery和angular同时使用被视为一个肮脏的行为。

0 人点赞