-
Notifications
You must be signed in to change notification settings - Fork 0
Description
经常有人问起vuejs和angularjs两个框架在实现数据绑定方面的异同点,这也是在面试当中经常问到的一个问题。虽然两个框架都用了很久,但能把这个问题讲清楚其实还是比较困难。虽然曾经仔细研究过框架实现数据绑定的底层代码和机制,但由于缺乏总结,仍然难以讲清楚。
vuejs
先来看一下vuejs是怎么实现的。用一个简单的词就是“数据劫持”。初看这个词,感觉好高深。其实其本质就是通过Object.defineProperty()来实现其具体的getter和setter方法,以达到劫持数据的模式来实现数据的响应式。这么说看起来又掉绕口,下面我们通过一个简单的例子来看看如何实现数据的响应式更新:
// html
<h2></h2>
// js
var obj = {};
var text = '';
var h2 = document.getElementsByTagName('h2')[0];
Object.defineProperty(obj, 'text', {
get: function () {
return text;
},
set: function (newVal) {
text = newVal;
h2.innerHTML = text;
}
});上面的代码很简单,我们先在html中创建了h2标签,然后在js中对对象obj的text属性设定了它的修饰符,实现了它的getter和setter。此时,我们在代码里或者console里修改obj.text的值,那么h2标签里的值也会一起跟着改变。
是不是很简单?响应式的数据绑定就这么实现了。而事实上,vuejs就是这么实现的。当然,vuejs中并不是这么简单,它还做了其它一些事情。Vue在初始化执行_init方法的时候会对数据进行reactive化,简单的讲就是对data进行数据依赖收集,通过我们熟知的pub/sub模式来形成一个个watcher,这些watcher有些来自于function,也有来自于视图。
angularjs
下面我们再来看一下angularjs中如何实现数据绑定的。在介绍之前,同样我们用一个词来总结它在angularjs中的实现,即“脏检查”机制。
我们都知道,在angularjs底层代码实现上,定义在$scope上的属性都会定义一个$watcher放到$watchers的数组当中,每一次修改该属性的时候,$watcher都会执行一遍$digest,然后把数据变化更新到视图当中。watch的属性会被封装为一个函数,每次digest的时候都会执行这个函数,然后对比与前一次执行的结果来判断是否dirty,进而执行对应的回调。这是angularjs实现数据绑定的基础。抛开这些容易把人搞晕的高深词汇,angularjs底层实现就是基于pub/sub这个设计模式来的。
前面的一段我们大致介绍了angularjs实现的基础,其中提到了很核心的dirty checking机制。那么都是在什么时候会启用dirty checking呢?
-
DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
-
XHR响应事件( $http )
-
浏览器Location变更事件 ( $location )
-
Timer事件( $timeout , $interval )
-
执行 $digest() 或 $apply()
这里angularjs有一个令人诟病的性能问题。如果scope上watcher数组特别长,那么每一次进入digest cycle,都要执行一遍,那么将引起严重的性能问题。其实,个人觉得在这方面大家是多虑了。angularjs团队没那么傻,在框架底层源码其实做了很大的优化,对于数组、对象等复杂数据的watcher方面,代码都做了最大的优化,以保证不会执行不必要的digest cycle. 对于这一点,并不是本文的重点,不再赘述。以后有时间可以单独讲一下。
结论
到此,vuejs和angularjs数据绑定的实现原理基本讲完,归结一下就是分别利用了“数据劫持”和“发布订阅者模式”。文章若有错误的地方,欢迎指正!