computed声明方式方式一: read-only
function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>>
比如:
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // error
源码
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
computed声明方式方式二: 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>
比如:
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
源码
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 //根据传入是否是函数来判断是否可读
) {
//调用effect函数,并且lazy为true,也就是懒加载
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
//set,第一次_dirty是false,因为get中改变的,调用trigger
//触发trigger,把自己加入依赖
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')
}
}
})
this[ReactiveFlags.IS_READONLY] = isReadonly
}
// this.effect()相当于vue2中的get(),收集依赖的过程
// _dirty 是为了lazy watchers
// Vue2中:watcher初始化会立即调用一次watcher.get方法,然后实际上可以通过传入{lazy:true}参数来延迟watcher.get方法的执行
get value() {
// 默认_dirty为true,数据是脏的,所以第一次会触发,调用effect()获取值,然后再_dirty变成false,触发track()函数
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)
}
}
// 如果传入的是一函数,则仅仅是get,也就是可读不可写,否则是带有get和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 new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
) as any
}
从源码可以很清楚看到,使用具有 get 和 set 函数的对象来创建可写的 ref 对象。
get是触发track函数; set是触发trigger函数;