Computed Declaration Method Method 1: Read-only
function computed<T> (getter: () => T) :Readonly<Ref<Readonly<T>>>
Copy the code
Such as:
const count = ref(1)
const plusOne = computed(() = > count.value + 1)
console.log(plusOne.value) / / 2
plusOne.value++ // error
Copy the code
The source code
export function computed<T> (getter: ComputedGetter<T>) :ComputedRef<T>
Copy the code
Computed Declaration Method Method 2: Writable
export interface ComputedRef<T = any> extends WritableComputedRef<T> {
readonly value: T
}
export interface WritableComputedRef<T> extends Ref<T> {
readonly effect: ReactiveEffect<T>
}
function computed<T> (options: { get: () => T; set: (value: T) => void }) :Ref<T>
export function computed<T> (options: WritableComputedOptions<T>) :WritableComputedRef<T>
Copy the code
Such as:
const count = ref(1)
const plusOne = computed({
get: () = > count.value + 1.set: val= > {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) / / 0
Copy the code
The source code
export type ComputedGetter<T> = (ctx? : any) = > T
export type ComputedSetter<T> = (v: T) = > void
export interface WritableComputedOptions<T> {
get: ComputedGetter<T>
set: ComputedSetter<T>
}
class ComputedRefImpl<T> { 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 // Check whether the incoming is readable based on whether it is a function
) {
// The effect function is called with lazy true
this.effect = effect(getter, {
lazy: true.scheduler: () = > {
//set, the first time _dirty is false, because get changed, call trigger
// Trigger the dependency
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')}}})this[ReactiveFlags.IS_READONLY] = isReadonly
}
// this.effect() is equivalent to get() in vue2, collecting dependencies
// _dirty is for lazy Watchers
// in Vue2: watcher initialization immediately calls the watcher. Get method once, and then you can actually delay the execution of the watcher
get value() {
// By default _dirty is true and the data is dirty, so the first time it is triggered, effect() is called to get the value, then _dirty becomes false and track() is triggered
if (this._dirty) {
this._value = this.effect()
this._dirty = false
}
track(toRaw(this), TrackOpTypes.GET, 'value')
return this._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
// If a function is passed in, it is only get, i.e. readable but not writable. Otherwise, it is an object with get and set
export function computed<T> (
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = __DEV__
? () = > {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
return newComputedRefImpl( getter, setter, isFunction(getterOrOptions) || ! getterOrOptions.set )as any
}
Copy the code
It is clear from the source that we use objects with get and set functions to create writable ref objects.
Get is the trigger track function; Set is the trigger function;