上一篇文章实现了v-bind, v-on指令以及methods等,这一篇来实现computed和watch;
1. computed
在Vue实例中通过this可以直接访问computed中的计算属性,而且它可以实现缓存,并且只有当依赖的数据变化才会更新;
1.1 ComputedWatcher
需要定义一个ComputedWatcher,它跟Watcher基本相同,只是再设置值的时候,需要跟新缓存的老值
class ComputedWatcher {
constructor(vm, expr, cb) {
this.vm = vm
this.expr = expr
this.cb = cb
this.value = this.get()
}
get() {
Dep.target = this
let oldValue = CompilerUtil.getVal(this.vm, this.expr)
Dep.target = null
return oldValue
}
update() {
let newVal = CompilerUtil.getVal(this.vm, this.expr)
if (newVal !== this.value) {
this.value = newVal // 跟Watcher不同
this.cb(newVal)
}
}
}
1.2 在取data中值是判断 是否是取computed 中的值
修改CompilerUtil中的取值函数getVal,加入判断
getVal(vm, expr) {
return expr.split('.').reduce((data, key) => {
if (key in vm.$computed) { // 1.2 添加
return vm.$computed[key].call(vm)
}
return data[key]
}, vm.$data)
}
1.3 在设置值前加入依赖,缓存旧值,并在数据更新的时候,更新computed 中的值以及视图
修改CompilerUtil中的取值函数setTextContent,加入判断
setTextContent(node, expr, vm) {
let content
let fn = this.updater['textUpdater']
let k = (/\{\{(.+?)\}\}/g).exec(expr)[1] //1.3 获取{{ }}
if (k in vm.$computed) {//1.3
// new ComputedWatcher的时候缓存旧值,加入依赖
let computed = new ComputedWatcher(vm, k, (newVal) => {
fn(node, newVal)
})
content = computed.value
} else {
content = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
new Watcher(vm, args[1], (newVal) => {
fn(node, newVal)
})
return this.getVal(vm, args[1])//school.age
})
}
fn(node, content)
}
至此computed功能基本完成
2. watch
2.1 修改Watcher类的update函数,接收上一次的值oldValue,更新vm.$watch中的函数传递newVal和oldValue
update(oldValue) {
let newVal = CompilerUtil.getVal(this.vm, this.expr)
let timer = null
if (newVal !== this.value) {
this.cb(newVal,oldValue)
if (this.expr in this.vm.$watch) {
vm.$watch[this.expr].call(this.vm, newVal, oldValue)
}
}
}
2.2 修改Dep类中的notify函数接收上一次的值oldValue,
notify(oldValue) {
this.subscribers.forEach(watcher => watcher.update(oldValue));
}
2.3 修改Observer类中的defindReactive函数中的Object.defineProperty的set函数
set: (newValue) => {
// 当赋的值和老值一样,就不重新赋值
if (newValue != value) {
let oldValue = value //2.3 添加
this.observer(newValue)//新值,做成响应式
value = newValue
dep.notify(oldValue) //2.3修改
}
}
到此为止watch的原理,也基本实现了。