思考以下代码
new Vue({el: '#example',data(){return{obj:{a:1}}},
})
当我们写下这行代码时,vue将我们在data内定义的obj对象进行依赖追踪.
具体做法为执行new Observer(obj)
//经过上面的代码,我们的obj对象会变为以下的样子
{obj:{a:1,__ob__:{ //Observer 实例dep:{Dep 实例subs:[ //存放 Watcher 实例new Watcher(),]}}}
}
我们来一步步实现看下。 1. 在obj
对象上新增__ob__
属性,值为Observe
类的实例,我们编写一个 def
函数,用来增加属性
function def(obj, key, val, enumerable) {Object.defineProperty(obj, key, {value: val,enumerable: !!enumerable,writable: true,configurable: true});
}
增加啥属性呢?之前提到了,我们需要增加一个 Observer
实例,实现如下
Observe 实现
class Observer {constructor(targetObject) {def(targetObject, '__ob__', this);//在 targetObject 上 添加 Observer 实例, setter时 通知该实例this.walk(targetObject)this.dep = new Dep()}walk(obj) {Object.keys(obj).forEach(key => {defineReactive(obj, key, obj[key])});}}function observe(data) {if (Object.prototype.toString.call(data) !== '[object Object]') {return}new Observer(data)
}function defineReactive(obj, key, val) {observe(val)Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter() {console.log('get');const ob = this.__ob__ob.dep.depend();return val},set: function reactiveSetter(newVal) {console.log('set');if (newVal === val) returnval = newValobserve(newVal)const ob = this.__ob__ob.dep.notify();},})
}function def(obj, key, val, enumerable) {Object.defineProperty(obj, key, {value: val,enumerable: !!enumerable,writable: true,configurable: true});
}
这里面牵扯到了 Dep,我们也把Dep实现下,
Dep
class Dep {constructor() {this.subs = []}addSub(sub) {this.subs.push(sub)}depend() {this.subs.push(Dep.target)}notify() {for (let i = 0; i < this.subs.length; i++) {this.subs[i].fn()}}
}Dep.target = null
Observer 类
主要做了以下事情
- 遍历 data 下的每一个属性,若是对象,则 执行 new Observer() ,在对象上新增
__ob__
属性,该属性的值为 Observer 的实例 - 劫持对象属性的变化,在 getter 的时候,拿到 Observer 实例的dep实例,执行dep.depend(),代码如下
const ob = this.__ob__ob.dep.depend();
看下 dep.depend()
做了些啥
this.subs.push(Dep.target)
将Dep.target
添加到 订阅数组内(this.subs)
也就是说,只要我们 Dep.target 赋值了,再执行 dep.depend()
,那么该值就会被添加到 dep 的 subs 数组内,比如
Dep.target =function test(){}
dep.depend()
// test 函数就算 Dep 的订阅者了
实现自动添加依赖
这个时候该 Watcher
出场了
Watcher 实现
const Dep = require('./Dep')class Watcher {constructor(vm, exp, fn) {this.vm = vmthis.exp = expthis.fn = fnDep.target = this//将自己挂载到 Dep.target,调用 Dep.depend时会读取该变量this.vm[exp]}
}module.exports = Watcher
根据一个小例子来理解 Watcher
const obj = {a: 1,b: {c: 2}
}new Observer(obj)
new Watcher(obj, 'a', () => {console.log('Watcher 回调执行')
})
obj.a='222'
流程如下: 1. 先观测 obj 对象(new Observer(obj)
) 2. 实例化Watcher
时,会执行Dep.target = this
,然后执行this.vm[exp]
,也就是取一次值,那么会触发 getter,将自身(Watcher实例)添加到dep的订阅者数组内
get: function reactiveGetter() {const ob = this.__ob__ob.dep.depend();return val},
最后,改变数据时候,触发setter
set: function reactiveSetter(newVal) {if (newVal === val) returnval = newValobserve(newVal)const ob = this.__ob__ob.dep.notify();},
执行ob.dep.notify()
notify() {for (let i = 0; i < this.subs.length; i++) {this.subs[i].fn()}
遍历 订阅者(subs)执行回调函数,整个流程结束
Leeesin/learn-vue-source-codegithub.com