computed的基本使用
computed 是组件的计算属性,它的含义是依赖于其他状态而生成的状态,与响应式紧密相关。
computed 有两种创建方式:
-
给 computed 函数传递一个 getter 方法创建 immutable reactive ref object const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value)
plusOne.value = 3
-
给 computed 函数传递一个有 get 和 set 方法的对象来创建一个 writable ref object const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value)
computed原理
computed 的特性就在于能够缓存计算的值(提升性能),只有当 computed 的依赖发生变化时才会重新计算,否则读取 computed 的值则一直是之前的值。computed 是怎么做到的呢?让我们一起来看看源码来解惑。
computed源码
源码地址:packages\reactivity\src\computed.ts
computed创建
computed 函数接收一个 getter 方法或者是一个含有 get 方法和 set 方法的对象,并返回一个 ref 对象。
export function computed<T>(
getter: ComputedGetter<T>,
debugOptions?: DebuggerOptions
): ComputedRef<T>
export function computed<T>(
options: WritableComputedOptions<T>,
debugOptions?: DebuggerOptions
): WritableComputedRef<T>
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
debugOptions?: DebuggerOptions
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
const onlyGetter = isFunction(getterOrOptions)
if (onlyGetter) {
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter)
if (__DEV__ && debugOptions) {
cRef.effect.onTrack = debugOptions.onTrack
cRef.effect.onTrigger = debugOptions.onTrigger
}
return cRef as any
}
ComputedRefImpl产生ref对象
通过上面的代码我们可以看出computed 函数返回的ref对象是执行构造方法 ComputedRefImpl 而创建的一个实例。 ComputedRefImpl 的构造方法一共做了两件事:
- 调用
effect 方法生成 watcher 监听函数并赋值给实例的 effect 属性。 - 设置ref对象是否为
readonly 。
class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
private _dirty = true
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
const self = toRaw(this)
trackRefValue(self)
if (self._dirty) {
self._dirty = false
self._value = self.effect.run()!
}
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
computed 缓存值的实现分析
我们发现声明一个 computed 时其实并不会执行getter方法,只有在读取 computed 值时才会执行它的 getter 方法,而这个方法实在构造函数 ComputedRefImpl 的 getter 方法定义实现的。
getter 方法会在读取 computed 值的时候执行(包含依赖收集),由于脏数据的开关,初始化时 _dirty 被设为 true ) ,在 getter 方法中会计算一遍computed 的值并设置self._dirty = false ,在数据源不发生变化的情况下之后再获取 computed 的值时由于 _dirty 是 false 就不会重新计算。这就是 computed 缓存值的实现原理。
computed重新计算值
在数据源发生变化时,在ComputedRefImpl 构造函数里为对象添加的effect 函数会给对象的响应式对象生成监听函数,并对 scheduler 进行了设置。 所以,当computed 内部依赖的状态发生改变,执行对应的监听函数,这其中自然会执行 scheduler 里的操作。而在 scheduler 中将 _dirty 设为了 true 。从而在下次取值时,进行重新计算。
结语
如果本文对你有一丁点帮助,点个赞支持一下吧,感谢感谢
|