《Vue进阶教程》第二十四课:优化
往期内容:
《Vue进阶教程》第十三课:实现依赖收集
《Vue进阶教程》第十四课:改进桶结构
《Vue进阶教程》第十五课:深入完善响应式系统之模块化
《Vue进阶教程》第十六课:深入完善响应式系统之单例模式
《Vue进阶教程》第十七课:支持分支切换
《Vue进阶教程》第十八课:避免死循环
《Vue进阶教程》第十九课:computed初步实现
《Vue进阶教程》第二十课:lazy懒执行
《Vue进阶教程》第二十一课:支持缓存
《Vue进阶教程》第二十二课:自定义更新(调度器)
《Vue进阶教程》第二十三课:渲染计算属性的结果
1) 封装
由于computed
函数最终会返回一个对象.
可以考虑将返回的对象封装成ComputedRefImpl
的实例
class ComputedRefImpl {
constructor(fn) {
this._value = null // 缓存
this._dirty = true // 标识
this.effect = new RectiveEffect(fn, () => {
if (!this._dirty) this._dirty = true
trigger(this, 'value')
})
}
get value() {
// 收集当前计算属性依赖的副作用函数
track(obj, 'value')
if (this._dirty) {
this._value = this.effect.run()
this._dirty = false
}
return this._value
}
}
// 这里先只考虑fn是函数的情况
function computed(fn) {
return new ComputedRefImpl(fn)
}
2) 扩展
考虑到计算属性是可以支持两种配置的. 进一步扩展支持配置(getter/setter)
class ComputedRefImpl {
constructor(getter, setter) {
this._value = null // 缓存
this._dirty = true // 标识
this.effect = new RectiveEffect(getter, () => {
if (!this._dirty) this._dirty = true
trigger(this, 'value')
})
this.setter = setter
}
get value() {
track(this, 'value')
if (this._dirty) {
this._value = this.effect.run()
this._dirty = false
}
return this._value
}
set value(newVal) {
return this.setter(newVal)
}
}
function computed(getterOrOptions) {
let getter
let setter
if (typeof getterOrOptions == 'function') {
getter = getterOrOptions
setter = () => {
console.warn('no setter')
}
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
return new ComputedRefImpl(getter, setter)
}
3) 解耦复用
在收集依赖时, 不能很好的观察到当前计算属性依赖的副作用函数.
可以考虑给ComputedRefImpl
加一个dep
属性, 用来保存哪些副作用函数引用了当前计算属性
而且, 在调用track
和trigger
时, 必须要传入两个参数. 这里我们人为创造了一个value
. 这种方式不够优雅
考虑对track
和trigger
进一步解耦
class ComputedRefImpl {
constructor(getter, setter) {
this._value = null // 缓存
this._dirty = true // 标识
this.dep = new Set()
this.effect = new RectiveEffect(getter, () => {
if (!this._dirty) this._dirty = true
triggerEffects(this.dep)
})
this.setter = setter
}
get value() {
trackEffects(this.dep)
if (this._dirty) {
this._value = this.effect.run()
this._dirty = false
}
return this._value
}
set value(newVal) {
return this.setter(newVal)
}
}
实现trackEffects
function trackEffects(dep) {
// 只有当activeEffect有值时, 才需要收集依赖
if (!activeEffect) return
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
实现triggerEffects
function triggerEffects(dep) {
if (!dep) return
const effects = [...dep]
effects.forEach(effect => {
if (effect !== activeEffect) {
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
}
})
}