响应式系统实现
这篇主要是配置computed,vue2实现是通过lazy和dirty这两个变量控制。
export class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean = false
public _cacheable: boolean
/**
* Dev only
*/
_warnRecursive?: boolean
constructor(
private getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean,
isSSR: boolean,
) {
this.effect = new ReactiveEffect(
() => getter(this._value),
() =>
triggerRefValue(
this,
this.effect._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect
? DirtyLevels.MaybeDirty_ComputedSideEffect
: DirtyLevels.MaybeDirty,
),
)
this.effect.computed = this
this.effect.active = this._cacheable = !isSSR
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
const self = toRaw(this)
if (
(!self._cacheable || self.effect.dirty) &&
hasChanged(self._value, (self._value = self.effect.run()!))
) {
triggerRefValue(self, DirtyLevels.Dirty)
}
trackRefValue(self)
if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty_ComputedSideEffect) {
if (__DEV__ && (__TEST__ || this._warnRecursive)) {
warn(COMPUTED_SIDE_EFFECT_WARN, `\n\ngetter: `, this.getter)
}
triggerRefValue(self, DirtyLevels.MaybeDirty_ComputedSideEffect)
}
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
// #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.x
get _dirty() {
return this.effect.dirty
}
set _dirty(v) {
this.effect.dirty = v
}
// #endregion
}
上面这个不是很好理解,用下面的理解
export class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean = false
// 数据是否更新的标识:缓存标识、脏数据标识,默认应该取值计算,所以是true
public _dirty = true
public _cacheable: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean,
isSSR: boolean
) {
// 将用户的getter放到effect中,这样能对getter函数进行依赖收集,activeEffect会变为getter生成的effect
// 传入scheduler调用函数,稍后 依赖的属性变化会调用此方法
// scheduler
this.effect = new ReactiveEffect(getter, () => {
// 稍后依赖属性变化会执行此调度函数
if (!this._dirty) {
// 2、依赖的值变化会更新dirty并触发更新
this._dirty = true
// 触发更新
triggerRefValue(this)
}
})
this.effect.computed = this
this.effect.active = this._cacheable = !isSSR
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// 获取原始对象
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
const self = toRaw(this)
// 1、取值的时候进行依赖收集!!!
trackRefValue(self)
// 第一次是true,开关开启,说明是脏值,执行函数,然后关闭开关
if (self._dirty || !self._cacheable) {
self._dirty = false
// 其实执行的是scheduler调度函数在其中触发更新triggerEffects,此处的run方法其实就是computed传入的匿名方法
self._value = self.effect.run()!
}
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
定义了 _value私有变量用于缓存上一次计算的值,定义了 _dirty变量用于表示是否需要重新计算值,为true时表示需要重新计算,默认是true;
在构造函数中,定义了effect,ReactiveEffect第二个参数称为 scheduler调度器,当依赖属性的值发生变化时会触发该方法的执行;
effect scheduler
// effect 函数
export function effect(fn, options: any = {}) {
const _effect = new EffectReactive(fn, options.scheduler)
... 省略其他
}
// class EffectReactive 中做以下修改
constructor(fn, public scheduler?) {
this.fn = fn
// 把scheduler 绑定在this当中,方便track中调用
this.scheduler = scheduler
}
// track 函数做以下修改
deps.forEach(effect => {
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
})