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,
isSSR = false
) {
// 格式化参数
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// 如果传过来的是一个函数(即函数式写法),那么就是只读的
const onlyGetter = isFunction(getterOrOptions)
if (onlyGetter) {
getter = getterOrOptions // 我们传过来的函数赋值给 getter
setter = __DEV__ // 不支持 setter, 否则会报错
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else { // 选项式写法 可读可写
getter = getterOrOptions.get
setter = getterOrOptions.set
}
// 将 getter 和 setter 传入这个类
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)
if (__DEV__ && debugOptions && !isSSR) {
cRef.effect.onTrack = debugOptions.onTrack
cRef.effect.onTrigger = debugOptions.onTrigger
}
return cRef as any
}
这段代码是 computed
函数内部的逻辑,用于根据传入的参数 getterOrOptions
创建计算属性的 getter
和 setter
,然后创建一个 ComputedRefImpl
实例并返回。
以下是代码的详细解释:
-
首先,定义了
getter
和setter
变量,用于后续存储计算属性的获取和设置函数。 -
通过检查
getterOrOptions
是否为函数来判断是函数式写法还是选项式写法。函数式写法表示计算属性是只读的,因此getterOrOptions
直接赋值给getter
变量,而setter
变量则被设置为一个函数。这个函数用于在开发环境下报错,提示用户不能对只读的计算属性进行写入操作。// 只读的计算属性,直接使用传入的 getter 函数 if (onlyGetter) { getter = getterOrOptions; // 传入的函数作为 getter setter = __DEV__ // 如果是开发环境,设置 setter 为一个警告函数 ? () => { console.warn('Write operation failed: computed value is readonly') } : NOOP; // 否则设置为空函数 NOOP }
-
如果不是函数式写法,表示计算属性是可读可写的。此时,从传入的
getterOrOptions
对象中提取get
和set
函数,分别赋值给getter
和setter
变量。} else { // 选项式写法,可以读写 getter = getterOrOptions.get; // 从选项对象中提取 getter setter = getterOrOptions.set; // 从选项对象中提取 setter }
-
创建
ComputedRefImpl
实例cRef
,将getter
、setter
、只读标志以及isSSR
参数传入。cRef
代表了最终的计算属性对象。// 创建 ComputedRefImpl 实例 const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
-
在开发环境下,如果传入了调试选项
debugOptions
并且不是服务端渲染(isSSR
为false
),则将调试选项中的onTrack
和onTrigger
回调函数分别赋值给cRef.effect.onTrack
和cRef.effect.onTrigger
,用于跟踪计算属性的依赖和触发情况。if (__DEV__ && debugOptions && !isSSR) { cRef.effect.onTrack = debugOptions.onTrack; cRef.effect.onTrigger = debugOptions.onTrigger; }
-
最后,将创建的
cRef
对象返回,它包含了计算属性的值、获取和设置函数等信息。return cRef as any;
总之,这段代码是 computed
函数内部的逻辑,用于根据传入的参数创建计算属性的 getter
和 setter
函数,并最终创建一个 ComputedRefImpl
实例,返回计算属性对象。根据不同的写法和配置,可以创建只读或可读可写的计算属性。
然后我们具体看一下 ComputedRefImpl
实例:
export class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean = false
public _dirty = true // 看是否是脏的,是否需要重新计算
public _cacheable: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean,
isSSR: boolean
) {
// 创建 ReactiveEffect 对象,用于管理计算属性的响应式依赖
// 依赖变化并且脏值为 false ,依赖变化这个函数才会执行
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
// 将计算属性与 ReactiveEffect 对象关联
this.effect.computed = this
this.effect.active = this._cacheable = !isSSR // 根据是否在服务器端渲染设置是否缓存计算结果
this[ReactiveFlags.IS_READONLY] = isReadonly // 标记是否为只读计算属性
}
// 收到脏值为 true ,直接执行 get value 函数
get value() {
// 计算属性的获取方法,用于获取计算属性的值
const self = toRaw(this) // 获取原始的计算属性对象,脱离 Proxy 代理
trackRefValue(self) // 跟踪计算属性的引用
if (self._dirty || !self._cacheable) {
// 如果属性标记为脏(需要重新计算)或者不可缓存,则重新计算属性值
self._dirty = false
self._value = self.effect.run()! // 运行计算属性的 effect 函数以计算新值
}
return self._value
}
set value(newValue: T) {
// 计算属性的设置方法,用于设置计算属性的值
this._setter(newValue) // 调用用户提供的 setter 函数
}
}
这段代码定义了 ComputedRefImpl
类,该类是 Vue 3 中计算属性(computed
)的实现的一部分。
对代码的主要解释如下:
-
ComputedRefImpl
类用于实现计算属性。它接受以下参数:getter
: 计算属性的获取函数,用于计算属性的值。_setter
: 计算属性的设置函数,用于设置计算属性的值。isReadonly
: 一个布尔值,表示计算属性是否只读。isSSR
: 一个布尔值,表示是否在服务器端渲染。
-
在构造函数中,创建了一个
ReactiveEffect
对象,该对象将getter
函数传递给它,并定义了一个回调函数。当计算属性的依赖发生变化时,这个回调函数将被触发。回调函数会将_dirty
属性设置为true
,表示计算属性需要重新计算,并调用triggerRefValue(this)
,通知相关依赖进行更新。 -
将创建的
ReactiveEffect
对象与当前的ComputedRefImpl
实例关联,以便后续进行管理和跟踪。 -
根据传入的参数,设置
effect
的active
属性和_cacheable
属性。active
表示是否需要在计算属性失活时停止计算,_cacheable
表示是否可以缓存计算结果。 -
定义
get value()
方法,用于获取计算属性的值。在获取属性值之前,会调用trackRefValue(self)
来跟踪计算属性的引用。如果计算属性被标记为脏(需要重新计算)或者不可缓存,将重新计算属性值,并将_dirty
设置为false
,以表示属性值已经计算完毕。 -
定义
set value(newValue: T)
方法,用于设置计算属性的值。它会调用用户提供的_setter
函数来进行属性值的设置。
总之,这段代码实现了计算属性的核心逻辑,包括属性值的获取和设置,以及属性值的计算和缓存管理。计算属性可以根据其依赖自动更新,并且可以选择是否设置为只读。