usage
There are two ways to use it: get only, whose return value is also read-only, and read and write
- Get: Passes a getter function. The return value of computed is read-only after the value is wrapped by ref.
const count = ref(1)
const plusOne = computed(() = > count.value + 1)
console.log(plusOne.value) / / 2
plusOne.value++ // error
Copy the code
- Set: You can also pass in an object with two functions, get and set, in which case the computed return value is writable.
Source of readable writable judgment is judged according to our incoming parameters, we can see, the is a function of the incoming if default is getter function, nature is a readable, and if the incoming object, note that here is not to function that is writable, you also have a set function, it also has a withdrawal in the source code:
// ...
return newComputedRefImpl( getter, setter, isFunction(getterOrOptions) || ! getterOrOptions.set// This corresponds to the constructor's readonly
) as any
constructor(getter: ComputedGetter
, private readonly _setter: ComputedSetter
, isReadonly: boolean
) {
/ /...
}
Copy the code
Type verification is performed with function overloading for two usages:
// read-only
function computed<T> (getter: () => T) :Readonly<Ref<Readonly<T>>>
// writable
function computed<T> (options: { get: () => T; set: (value: T) => void }) :Ref<T>
Copy the code
Watch and watchEffect are similar. Let’s take a closer look at the ComputedRefImpl class that returns this Ref
ComputedRefImpl
First post the source code:
class ComputedRefImpl<T> {
// some initial var...
constructor(getter: ComputedGetter
, private readonly _setter: ComputedSetter
, isReadonly: boolean
) {
// ..
}
get value() {
// ...
}
set value(newValue: T) {
// ... }}Copy the code
In addition to constructors and get and set, there are also calls to effect, track, trigger methods, the source code is specifically placed in a file effect. Ts, which is specialized in dealing with side effects, including the corresponding dependency storage
This call to effect wraps around the getter to create a cache, in the words of the document: calculated properties are cached based on their reactive dependencies, and they are reevaluated only when the associated reactive dependencies change
this.effect = effect(getter, {
lazy: true.scheduler: () = > {
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')}}Copy the code
There are two configuration items passed in: a lazy and a scheduler. I don’t know what the scheduler is at first, but it translates to a scheduler. A scheduler specifies how to run side effects functions, and there is a trigger function inside that triggers SET operations. So, as the name implies, the set operation is performed, and the _dirty variable is used to control whether the cache is read or the new value
get value() {
if (this._dirty) {
this._value = this.effect()
this._dirty = false
}
track(toRaw(this), TrackOpTypes.GET, 'value')
return this._value
}
Copy the code
Then we go to the effect method, which is created using the createReativeEffect function
function createReactiveEffect<T = any> (fn: () => T, options: ReactiveEffectOptions) :ReactiveEffect<T> {
const effect = function reactiveEffect() :unknown {
/ /...
} asReactiveEffect effect.id = uid++ effect.allowRecurse = !! options.allowRecurse effect._isEffect =true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect
}
Copy the code
We can see that a reactiveEffect function is returned with the following properties:
_isEffect: true
id: number
active: boolean
raw: () = > T
deps: Array<Dep>
options: ReactiveEffectOptions
allowRecurse: boolean
Copy the code
track & trigger
The data structure of the side effects dependencies looks like this:
weakmap:(target, Map(key,Set[effect,effect]) )
target—–>key——>dep
Track is used to track collection dependencies and trigger is used to trigger responses.