VUE2.0如何追踪数据变化?

作者: 娜姐聊前端 | 来源:发表于2017-02-26 17:19 被阅读4876次

    我们知道Vue.js和angular(特指vue 2.0和angular 1),都实现了数据双向绑定。而为了支持双向绑定,就必须时刻追踪数据变化并及时响应到UI上,反之亦然。

    Angular 1 中,采用脏检查机制,缺点是:当watcher越来越多时,作用域内每一次变化,所有watcher都要重新计算。如果一些watcher引发了另外的更新,那么,digest cycle 可能要运行多次。一般来说,不建议在一个页面上绑定大于1000个watcher。

    Vue采用更加优雅的方式来解决:数据劫持+发布订阅者模式。

    1. 数据劫持

    Vue通过Object.defineProperty()设置对象的存储器属性,即setget。这样可以拦截数据,做一些额外的事情。比如设置/更新时,添加对该属性感兴趣的订阅者;读取属性时,通知关系该属性的订阅者更新数据。

    2. 发布订阅者模式

    先看官网上的一张图(来自:https://vuefe.cn/v2/guide/reactivity.html):

    data.png

    主要分为四部分:

    1. Data:也就是数据属性观察者(observer),它劫持属性变化,并负责
      • 添加订阅者(watcher)到订阅者容器(Dependency)
      • 数据改变时,通知订阅者容器发布更新通知。
    2. Dependency:一个订阅者容器,负责维护watcher,并通知watcher做更新操作。
    3. Watcher:某个属性数据的监听者/订阅者,一旦数据有变化,它会通知指令(directive)重新编译模板并渲染UI。
    4. Directive(Component Render Function):指令负责将model和DOM关联起来,在watcher触发下,它可以根据最新的数据重新编译模板,并最终重绘UI(vue2.0在重绘DOM时,采用虚拟DOM树机制,用最小的开销更新UI)。

    下面是一张更加详细的剖析图(图内的方法名只作为示例):

    vue-data.png

    从上图可以更清楚的看到:

    • 每个指令都对应一个watcher(在编译指令时,就会初始化这个watcher)。一旦调用watcher.update(),即会通知指令重新编译模板。
    • Dep对象维护了一个watcher array。
    • 数据对象的每个属性,都包含一个Dep实例对象,用于存储关心该属性变化的watchers。
    • 在model--->UI渲染过程中,通过数据属性的get函数,可以添加相对应的watcher到Dep对象中。
    • 当触发UI更新操作(比如,input框输入某些内容),即UI--->Model--->UI这个过程中,首先触发对应数据属性的set函数,然后订阅者容器Dep对象发布消息通知notify,随后,所有订阅者watchers调用update(),从而通知模板编译器Directive Compiler对相应的指令进行重新编译,DOM重绘。
    小贴士:
    模板编译时,会把html模板编译成render函数。
    所以,如果直接用render函数来创建组件html,编译速度会更快。
    

    3. 其他:异步更新队列

    官方文档上,还提到了异步更新队列机制。也就是数据变化时,先缓冲watcher在当前事件循环中,并去掉重复数据(避免同一个watcher被多次触发)。然后,在下一次事件循环中(next tick),再真正的更新DOM。

    官网上的例子很清楚的解释了这个“延迟过程”:

    HTML:
    <div id="example">{{message}}</div>
    
    JS:
    var vm = new Vue({
      el: '#example',
      data: {
        message: '123'
      }
    })
    vm.message = 'new message' // 更改数据
    vm.$el.textContent === 'new message' // false 这个时候DOM节点还没更新
    Vue.nextTick(function () {
      vm.$el.textContent === 'new message' // true 在下一个Tick中,DOM节点才会更新
    })
    

    小结

    一句话总结Vue.js如何实现数据双向绑定:通过ES5新特性Object.defineProperty()的存储性属性setget实现了数据劫持,并采用发布-订阅者设计模式,利用一系列watcher对象监听数据变化并通知DOM更新。

    微信公众号:

    娜姐聊前端.jpg

    相关文章

      网友评论

        本文标题:VUE2.0如何追踪数据变化?

        本文链接:https://www.haomeiwen.com/subject/yewowttx.html