这篇是对angularJS的一些疑点回顾,是对目前angularJS开发的各种常见问题的整理汇总。如果对文中的题目全部了然于胸,觉得对整个angular框架应该掌握的七七八八了。希望志同道合的通知补充内容
Angular 的数据绑定采用什么机制,详述原理?脏检查机制。阐释脏检查机制,必须先了解如下问题。
单向绑定(ng-bind) 和 双向绑定(ng-model) 的区别?ng-bind 单向数据绑定($scope -> view),用于数据显示,简写形式是 {{}}。
两者的区别在于页面没有加载完毕 {{val}} 会直接显示到页面,直到 Angular 渲染该绑定数据(这种行为有可能将 {{val}} 让用户看到);而 ng-bind 则是在 Angular 渲染完毕后将数据显示。
ng-model 是双向数据绑定($scope -> view and view -> $scope),用于绑定值会变化的表单元素等。
双向数据绑定是 AngularJS 的核心机制之一。当 view 中有任何数据变化时,会更新到 model ,当 model 中数据有变化时,view 也会同步更新,显然,这需要一个监控。
双向数据绑定的原理?
Angular 在 scope 模型上设置了一个 监听队列,用来监听数据变化并更新 view 。
每次绑定一个东西到 view 上时 AngularJS 就会往 $watch 队列里插入一条 $watch,用来检测它监视的 model 里是否有变化的东西。
当你写下表达式如{{ val }}时,AngularJS在幕后会为你在scope模型上设置一个watcher(表达式将被 Angular 编译成一个监视函数),它用来在数据发生变化的时候更新view。这里的watcher和你会在AngularJS中设置的watcher是一样的:
$scope.$watch('val', function(newValue, oldValue) { //update the DOM with newValue });
将数据附加到 Scope 上,数据自身不会对性能产生影响,如果没有监视器来监视这个属性,那个这个属性在不在 Scope 上是无关重要的;Angular 并不会遍历 Scope 上的属性,它将遍历所有的观察器。
每个监视函数是在每次 $digest 过程中被调用的。因此,我们要注意观察器的数量以及每个监视函数或者监视表达式的性能。
$digest循环是在什么时候以各种方式开始的?
当浏览器接收到可以被 angular context 处理的事件时,$digest 循环就会触发,遍历所有的 $watch,最后更新 dom。
举个栗子
<button ng-click="val=val+1">increase 1</button>click 时会产生一次更新的操作(至少触发两次 $digest 循环)
按下按钮
浏览器接收到一个事件,进入到 angular context
$digest 循环开始执行,查询每个 $watch 是否变化
由于监视 $scope.val 的 $watch 报告了变化,因此强制再执行一次 $digest 循环
新的 $digest 循环未检测到变化
浏览器拿回控制器,更新 $scope. val.新值对应的 dom
在调用了$scope.$digest()后,$digest循环就开始了。假设你在一个ng-click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地通过调用$digest()来触发一轮$digest循环。当$digest循环开始后,它会触发每个watcher。这些watchers会检查scope中的当前model值是否和上一次计算得到的model值不同。如果不同,那么对应的回调函数会被执行。调用该函数的结果,就是view中的表达式内容(译注:诸如{{ val }})会被更新。除了ng-click指令,还有一些其它的built-in指令以及服务来让你更改models(比如ng-model,$timeout等)和自动触发一次$digest循环。
目前为止还不错!但是,有一个小问题。在上面的例子中,AngularJS并不直接调用$digest(),而是调用$scope.$apply(),后者会调用$rootScope.$digest()。因此,一轮$digest循环在$rootScope开始,随后会访问到所有的children scope中的watchers。
通常写代码时我们无需主动调用 $apply 或 $digest 是因为 angular 在外部对我们的回调函数做了包装。例如常用的 ng-click,这是一个指令(Directive),内部实现则 类似 于
DOM.addEventListener('click', function ($scope) { $scope.$apply(() => userCode()); });可以看到:ng-click 帮我们做了 $apply 这个操作。类似的不只是这些事件回调函数,还有 $http、$timeout 等。我听很多人抱怨说 angular 这个库太大了什么都管,其实你可以不用它自带的这些服务(Service),只要你记得手工调用 $scope.$apply。
现在,假设你将ng-click指令关联到了一个button上,并传入了一个function名到ng-click上。当该button被点击时,AngularJS会将此function包装到一个wrapping function中,然后传入到$scope.$apply()。因此,你的function会正常被执行,修改models(如果需要的话),此时一轮$digest循环也会被触发,用来确保view也会被更新。