为什么你的 Angular 双向数据绑定会失效?

Angular双向数据绑定原理探究。

文章源码引用较多,觉得难以理解可以直接跳到末尾总结处。

接触过Angular的人一定会对其“双向数据绑定”的特性印象深刻,而使用过的人更会对莫名其妙出现的双向数据绑定失效的“坑”所困扰。例如下面一段代码:

源码地址:http://jsbin.com/xogosim/edit?html,js,console,output

如果上面代码中的两个问题你都知道答案,那么你可以跳过下面的内容,如果并不完全清楚,那么我们接着往下说~

双向数据绑定,指的是视图和模型之间的映射关系。双向即 视图 ==> 模型模型 ==> 视图 两个方向。

我们以Angular1.3为例,探究一下这个问题。

视图 ==> 模型

抛开Angular不说,如果我们要实现视图修改时触发模型的修改,很简单,事件(键盘事件、鼠标事件、UI事件)监听就能实现。而Angular会不会也是这么实现的?

最常用的场景便是表单元素的数据绑定,当元素的值发生变化时我们要通知模型层(比如校验、联动),例如用于实现这一功能的 ngModel 指令。

但是我们如果直接找到ngModel的源码,并没有找到直接的事件绑定,依赖ngModelOptions指令倒是有一段代码绑定了事件

可是平常没使用ngModelOptions的时候也能同步元素的修改,难道是一开始就想错了?

回忆一下Angular定义指令的时候,不光有像ngModel这样通过属性定义,也有直接定义成元素的,例如form就是一个指令。而最常用最简单的就是把ngModel用在input元素上,不,应该是input指令。

于是找到input指令的代码

发现只要nhgModel指令存在的时候,它就会根据type属性执行一段函数。

我们找到inputType.text这个函数之后,层层追寻…

终于找到了它在绑定事件的证据,而且还很智能,根据浏览器对事件的支持情况来进行绑定。

发现绑定的事件都执行了一个函数:$setViewValue。继续查找,发现调用ngModelSet函数来修改模型。

模型 ==> 视图

我们再次抛开Angular,回到原生实现,如果我们想要修改视图也比较简单,获取dom元素并修改对应的属性。

再找一个在Angular中将模型值同步到dom上的指令ngBind

发现其在scope.$watch回调函数中来修改dom元素的文本内容。那我们可以大胆地推测,应该是在修改了对应的$scope属性值之后,触发了scope.$watch调用了ngBindWatchAction回调函数才导致页面元素文本变化的。

从源码中可以看到,当我们在调用$watch监控变量的时候,其实是创建了一个watcher对象,并将其放入$scope.$$watchers数组中。

那么谁会用到这个数组,并且其中的回调函数呢?

这个代码有点难找,直到找到一个叫做$digest的函数定义。

简单概括一下这段代码,遍历$scope.$$watchers,判断如果需要检测的表达式的值(可以理解为$scope的属性)发生了修改,那么执行对应回调函数(比如ngBindg中的ngBindWatchAction)。

修改$scope对应的属性,并调用$scope.$digest。完成这两个条件即可同步模型数据到视图,修改dom元素。换句话说,这两个条件缺一不可。而调用$scope.digest这一过程,我们一般叫做脏值检测

有人可能会说我调用$scope.$apply也可以啊~

理论上来说,用$scope.$digest完成的手动试图同步都可以用$scope.$apply,但是他们之间还是有区别。

区别就在于,$apply是对$rootScope及子作用域做脏值检测,意味着性能消耗更大。支持回掉函数算是一个好处。

总结

视图 ==事件绑定==> 模型

模型 <==脏值检测== 模型

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

1 1 收藏 评论

关于作者:亚里士朱德

JSP工程师,多年大型国际项目开发经验。WEB前端工程师,擅长PC端以及移动端开发。js全栈工程师,熟悉node.js、mongoDB。开发者头条top10专栏作者慕课网签约讲师个人博客:yalishizhude.github.io 个人主页 · 我的文章 · 19 ·     

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部