Vue源代码笔记(一)数据绑定

VUE数据绑定介绍 数据绑定是vue的基础核心之一,本文以Vue对象(当然也包含VueComponent)里的data数据绑定为例,阐述整个绑定的过程。 Vue的数据绑定由三部分组成, Observer 监听Object里属性的变化,一旦有属性发生变化,会发布带有新值的通知。它是一个发布者的角色。 Watcher 接收新值的通知,对新值进行处理,比如刷新控件值(如果控件值绑定data里的属性)。它是一个订阅者的角色。 Dep Observer和Water的粘合剂。 Observer的通知是发给Dep,然后Dep再转发给Watcher。 而Watcher作为订阅者,不是直接注册在Observer里,而是Dep里。Dep可接收多个Watcher,一定程度来说也是个Watcher管理器。 以下是三者之间的关系图 以下是来自于Vue的本身自带的例子,由一个index.html和app.js组成。通过这个例子我们来看一下,整个数据绑定的过程。 index.html 复制代码 1 2 3 4 5 6 test 7 8 9
10 {{message}} 11 12
13 14 15 16 17 18 复制代码 app.js 复制代码 1 Vue.component('button-counter', { 2 data: function () { 3 return { 4 count: 0, 5 } 6 }, 7 methods:{ 8 onClick:function(){ 9 this.count++; 10 }, 11 }, 12 template: '
' 13 }) 14 15 var app = new Vue({ 16 el: '#app', 17 data: { 18 message: 'Hello Vue!' 19 } 20 }) 复制代码 运行代码后见下图,我们可以看见例子(index.html)中的
{{message}} ...
message属性被监听和绑定订阅者。 Vue利用Object.defineProperty来监听对象属性值的变化和对变化的通知。 对用于Object.defineProperty里定义的两个方法get和set,分别对应了Watcher对象订阅和属性变化通知给Watcher对象 Watcher对象的订阅过程 其中,get: function reactiveGetter方法,用于Watcher订阅此属性变化通知。get方法会在该属性值触发读(read)动作的时候触发。以上图中的message为例,当发生类似代码 “let val = message"或者"message"时,就会触发get函数。 从上面的get: function reactiveGetter方法代码中,我们可以看到表示当Dep.target不为空时,就进行Watcher的订阅。Dep.target实际上指向的是一个Watcher对象。 当调用类似下列代码时 Dep.target = watcher; // Watcher对象 this.messgae; 就会触发以下代码 复制代码 1 get: function reactiveGetter () { 2 const value = getter ? getter.call(obj) : val 3 if (Dep.target) { //Dep.taget指向一个Watcher对象 4 dep.depend() //当Dep.target不为空时,调用Dep对象(dep)的depend()方法来进行watcher的订阅 5 if (childOb) { 6 childOb.dep.depend() 7 if (Array.isArray(value)) { 8 dependArray(value) 9 } 10 } 11 } 12 return value 13 }, //代码出自Vue源代码目录下的/src/core/instance/index.js 复制代码 我们再来看下depend做了什么 复制代码 1 export default class Dep { 2 static target: ?Watcher; 3 id: number; 4 subs: Array; 5 6 constructor () { 7 this.id = uid++ 8 this.subs = [] 9 } 10 11 addSub (sub: Watcher) { //将Watcher对象放入subs数组中,完成订阅动作,有属性变化后,Dep通知subs数组里所有的watcher对象。 12 this.subs.push(sub) 13 } 14 15 removeSub (sub: Watcher) { 16 remove(this.subs, sub) 17 } 18 19 depend () { //Watcher注入Dep对象 20 if (Dep.target) { 21 Dep.target.addDep(this) 22 } 23 } 24 25 notify () { 26 // stabilize the subscriber list first 27 const subs = this.subs.slice() 28 for (let i = 0, l = subs.length; i < l; i++) { 29 subs[i].update() 30 } 31 } 32 } 33 34 // the current target watcher being evaluated. 35 // this is globally unique because there could be only one 36 // watcher being evaluated at any time. 37 Dep.target = null 38 const targetStack = [] //代码出自Vue源代码目录下的/src/core/observer/dep.js 复制代码 从上面的Dep代码中可以看出,调用了Watcher对象(Dep.target)的addDep方法注入Dep对象,来订阅属性变化通知。 接下来,我们再看一下addDep做了些什么。 复制代码 1 export default class Watcher { 2 3 ............ 4 5 /** 6 * Add a dependency to this directive. 7 */ 8 addDep (dep: Dep) { 9 const id = dep.id 10 if (!this.newDepIds.has(id)) { 11 this.newDepIds.add(id) 12 this.newDeps.push(dep) 13 if (!this.depIds.has(id)) { 14 dep.addSub(this) 15 } 16 } 17 } 18 19 ............. 20 21 } 22 23 //代码出自Vue源代码目录下的/src/core/observer/watcher.js 复制代码 我们可以从addDep方法里看到,最终调用了Dep的addSub方法。结合上面已列出的Dep代码。最终将watcher对象放入了Dep对象的subs数组中,完成了订阅动作。 Watcher对象接收属性值变化通知 set: reactiveSetter方法,在属性值执行写操作时(就是被赋值),会被触发。意味着类似代码"this.message='hello again' "就会触发set函数,执行下列代码 复制代码 1 set: function reactiveSetter (newVal) { 2 const value = getter ? getter.call(obj) : val 3 /* eslint-disable no-self-compare */ 4 if (newVal === value || (newVal !== newVal && value !== value)) { 5 return 6 } 7 /* eslint-enable no-self-compare */ 8 if (process.env.NODE_ENV !== 'production' && customSetter) 9 { 10 customSetter() 11 } 12 if (setter) { 13 setter.call(obj, newVal) 14 } else { 15 val = newVal 16 } 17 childOb = !shallow && observe(newVal) 18 dep.notify()//通知watcher对象,value值发生变化。 19 } //代码出自Vue源代码目录下的/src/core/instance/index.js 复制代码 在set函数中,调用dep.notify将最新的属性值通知dep对象里的所有watcher(保存在subs数组中),watcher对象调用update方法更新视图(参照上面提供的Dep代码)。 总结 1.Vue数据绑定,由Observer,Dep和Watcher组成。 Observer监测属性变化,发送变化通知。 Watcher订阅变化通知,根据通知里的最新属性值,更新视图。 Dep链接Observer和Watcher,转发Observer的通知到Watcher,Watcher通过Dep订阅Observer的通知。 2.Observer利用Object.defineProperty定义的get方法,监控属性的读操作。在读操作中调用get方法,如果Dep.target指向了一个Watcher对象,就调用Dep.depend-->Watcher.addDep-->Dep.addSub订阅属性变化通知。 3.Observer利用Object.defineProperty定义的set方法,监控属性的写操作(即更新属性值),在写操作中调用set方法,set方法里调用Dep.notify-->Watcher.update来用更新的值更新视图。https://www.cnblogs.com/andreitang/p/10158494.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信