编写一个mini观察器来解析Vue2.0的响应式系统。
可能大部分人都已知道了Vue2.0是采用Object.defineProperty()这个API进行实现,现在我们从0开始通过Object.defineProperty()编写一个mini观察器理解Vue响应式的原理思路。
1、getter 和 setter
实现一个转换函数(或者说是拦截函数),对对象中的属性进行set和get。
使用log查看是否有执行
function convert(obj) {
Object.keys(obj).forEach(key => {
let val = obj[key];
Object.defineProperty(obj, key, {
get() {
console.log('get val')
return val;
},
set(newVal) {
console.log(`set ${newVal} to ${key}`)
val = newVal;
}
})
})
}
2、依赖追踪
就是追踪当data改变时哪些代码应该执行。
定义一个Dep
类,至少应该包含depend(收集)和 notify(唤醒)这两个方法。
class Dep{
depend() {}
notify() {}
}
同时需要定义一个数组用于保存已经收集的信息
class Dep{
constructor() {
this.subs = new Set()
}
depend() {
// 判断是否存在更新函数
if(activeUpdate) {
this.subs.add(activeUpdate)
}
}
notify() {
this.subs.forEach(sub => sub())
}
}
写一个获取更新函数的方法。
let activeUpdate
function run(update) {
function wrapper() {
activeUpdate = wrapper;
update();
activeUpdate = null;
}
wrapper();
}
可能上面的代码有点难懂,我们先整合一下setter和getter中的方法,在结合着理解。
在get方法中添加依赖方法,在set方法中添加唤醒方法,可能有些看过源码的好兄弟们反映过来了,这不就是observe方法嘛。是的,那就改一下函数名称。
function observe(obj) {
Object.keys(obj).forEach(key => {
let val = obj[key];
let dep = new Dep()
Object.defineProperty(obj, key, {
get() {
// 收集
dep.depend()
console.log('get val')
return val;
},
set(newVal) {
// 判断是否更新
const hasChanged = val !== newVal
if(hasChanged) {
console.log(`set ${newVal} to ${key}`)
val = newVal;
dep.notify()
}
}
})
})
}
我们重点看一下get方法中的dep.depend()
。它是怎么将代码进行收集的呢?
先随便定义一个数据进行响应式处理
const state = { count: 0 }
observe(state)
run(() => { console.log(` count is ${state.count}`) })
我们都知道js是单线程
的,在同一时间只能执行一个函数。
我们在执行run(() => { console.log(
count is ${state.count}) })
这个方法时,执行的是run 内部的 wrapper 方法。 给activeUpdate
赋值为wrapper()
, 然后执行update()
方法。
在update()
方法中我们访问了state.count
属性,就会调用get()
方法,因此会执行dep.depend()
,在类Dep
中的depend()
方法中就可以根据activeUpdate
是否为undefined
进行依赖添加。
最后改变count的值就可以发现可以监听到数据的变化。
state.count = 1