前言
在现在的mvvm框架中, 有一个很普遍的功能 就是数据的双向绑定, 即响应式。那么这样的功能是怎样的实现的呢? 在阅读了染陌同学的《剖析 Vue.js 内部运行机制》后, 在这里复习一下。
实现
原理比较简单, 就是通过Object.defineProperty将需要响应式的对象的属性设置为get和set, 并在get所在闭包中, 通过dep对象进行依赖收集; 在set中调用dep中的notify方法,更新收集来的watcher对象(遍历watchers数组, 调用watcher的更新方法)。
Dep类——用于收集的类
class Dep {
constructor() {
this.subs = []//用于存放watcher对象的数组
}
addSub (sub) {
this.subs.push(sub)
}
notify(){
this.subs.forEach(sub=>{
sub.update()
})
}
}
Dep.target = null
Watcher类——用于更新组件视图的类
class Watcher {
constructor(){
Dep.target = this
}
update(){
console.log("视图更新了")
}
}
defineReactive函数
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
dep.addSub(Dep.target)
return val
},
set: function reactiveSetter(){
if(newVal === val) return
val = newVal
dep.notify()
}
})
}
observer方法
function observer(value) {
if(!value || (typeof value !== 'object')) {
return
}
Object.keys(value.forEach).forEach(key => {
defineReactive(value, key, value[key])
})
}
简洁版Vue构造函数
class Vue {
constructor(options) {
this._data = options.data
observer(this._data)
new Watcher();
}
}
最后
let o = new Vue({
data: {
test: 'test'
}
})
以上new了一个Vue对象,并且this._data已经被响应式了。
o._data.test//需要向get一下, 让getter所在的闭包中的dep对其收集
o._data.test = 'hello' //执行setter里的dep.notify进行更新, 最后会调用watcher中的update函数,更新视图