computed
const count = ref(1)
// 接受一个 getter 函数,并为从 getter 返回的值返回一个不变的响应式
const a = computed(() => count.value + 1)
// 也可以使用具有 `get` 和 `set` 函数的对象来创建可写的 ref 对象
const b = computed({
get: () => count.value + 1,
set: val => { count.value = val - 1 }
})
function computed(getterOrOptions) {
let getter
let setter
if (isFunction(getterOrOptions)) {
// 一个 getter 函数,这种 computed 是只读的
getter = getterOrOptions
setter = __DEV__? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
// 设置了 getter 和 setter 函数,这种 computed 是可写的
getter = getterOrOptions.get
setter = getterOrOptions.set
}
return new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
)
}
ComputedRefImpl
class ComputedRefImpl<T> {
private _value
private _dirty = true
public readonly effect
public readonly __v_isRef = true;
public readonly [ReactiveFlags.IS_READONLY]
constructor(getter, private readonly _setter, isReadonly) {
// lazy延迟执行,不会立即执行 getter
// scheduler 在派发时执行 getter,并且派发 computed 的所有依赖通知 computed 被修改
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')
}
}
})
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
const self = toRaw(this)
if (self._dirty) {
self._value = this.effect()
self._dirty = false
}
track(self, TrackOpTypes.GET, 'value')
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
computed 触发流程
<div> {{ b }} </div>
let a = ref(1)
// computed b 中访问 a 时,a 的 proxy.get 就会收集 b 的依赖
let b = computed(()=>{ return a + 1 })
a.value =2
// 派发该 a 下的所有依赖
if (effect.options.scheduler) {
effect.options.scheduler(effect)
} else {
effect()
}
stateDiagram-v2
a.value=2 --> a.value
a.value --> 派发依赖b
派发依赖b --> b
b --> 派发依赖模板b
派发依赖模板b --> 模板b
模板b --> 访问b
访问b --> b
b--> 执行getter
执行getter--> 访问a
访问a --> a.value
依赖触发流程也与Vue2.5.17后的一致
ComputedRefImpl : {
// 依赖
effect:reactiveEffect(),
// 响应式数据
__v_isReadonly: true,
// ref
__v_isRef: true,
// 是否使用缓存
_dirty: false,
// 上一次执行的参数
_value:''
}
-
延时计算,只有当我们访问计算属性的时候,它才会执行 computed getter 函数计算;
-
缓存,它的内部会缓存上次的计算结果 value。而且只有使用的响应式变量改变与第一次访问, dirty 为 true 时才会重新计算。如果访问计算属性时 dirty 为 false,那么直接返回这个 value。
-
由于 computed 这种巧妙的派发机制,只要你不循环引用,无论嵌套多少层都是可以的。