Basic use of computed
computed
It is a computed property of a component, which means a state generated depending on other states, and is closely related to responsiveness.
computed
There are two ways to create:
-
Pass a method to
computed
the functiongetter
to createimmutable reactive ref object
const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value = 3 // error,因为plusOne是immutable ref obj
-
computed
Pass an object withget
andset
methods to the function to create a writable ref objectconst count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0
computed principle
computed
The characteristic of is that it can cache the calculated value (improving performance), and computed
it will only be recalculated when the dependency of changes, otherwise the read computed
value will always be the previous value. computed
How did you do it? Let's take a look at the source code to clarify.
computed source code
Source address:packages\reactivity\src\computed.ts
computed create
computed
The function takes a getter
method or an object with get
methods and set
methods , and returns a ref
object.
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)
// 当getterOrOptions为函数的时候,说明是只读computed,会将其赋值给与getter
if (onlyGetter) {
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
// 当getterOrOptions为对象的时候,说明是是自定义的getter setter,会将set和get分别赋值给setter,getter。
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 generates ref objects
From the above code, we can see that computed
the ref object returned by the function is an instance created ComputedRefImpl
by . ComputedRefImpl
The construction method does two things in total:
- Call
effect
the method to generatewatcher
a listener function and assign it to the effect property of the instance. - Sets whether the ref object is
readonly
.
class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
private _dirty = true // 脏数据flag 用来判断是否需要重新计算
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true // ref响应式对象标识
public readonly [ReactiveFlags.IS_READONLY]: boolean // ReactiveFlags只读标识
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
// 调用 ReactiveEffect 方法生成监听函数并赋值给实例的 effect 属性
this.effect = new ReactiveEffect(getter, () => {
// 由于初始化时,计算过一次computed值,_dirty已经设为了false
// 所以当内部依赖发生变化时,会由此进入,设置_dirty为true,这样获取computed值时会再次计算
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
// 设置ref对象是否为 readonly
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
const self = toRaw(this)
trackRefValue(self) // 依赖收集
// 初始化时,_dirty为true,会计算一次computed值
if (self._dirty) {
// 设置_dirty为false, 防止再次获取时重新计算,这就是 computed 缓存值的实现原理
self._dirty = false
self._value = self.effect.run()!
}
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
computed
Implementation Analysis of Cache Value
We found that the getter method is not actually executed computed
when , computed
its getter
method is read, and this method is implemented in the methodComputedRefImpl
definition of the constructor.getter
getter
The method will be executed when reading computed
the value (including dependent collection). Due to the switch of dirty data, _dirty
it will be set to true
) at the time of initialization. In getter
the method computed
the value of will be calculated and set self._dirty = false
. After the data source does not change When computed
retrieving the value of is will not be recalculated since is_dirty
is . false
This is how computed
cached values are implemented.
computed recomputed value
When the data source changes, the function ComputedRefImpl
added to the object in the constructor effect
will generate a listener function for the responsive object of the object, and scheduler
set .
Therefore, when computed
the state of the internal dependencies changes, the corresponding monitoring function is executed, which will naturally execute the operations scheduler
in . And in scheduler
the _dirty
is set to true
. Therefore, it will be recalculated the next time the value is taken.
epilogue
If this article is of any help to you, please give it a thumbs up to support it, thank you