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

我们知道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,编译速度会更快。

实例代码:https://github.com/DMQ/mvvm

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更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容