computed:
一、入口函数:
代码的核心是实例化对象 ComputedRefImpl
;有一个需要注意的点是会判断 getterOrOptions
是否是function,不是的话会直接调用并采用对象里的方法(就是计算属性的可写方式传入的代码),大部分我们都是采用可读的方式调用计算属性就是传入一个函数方法。
export function computed<T>(
// getterOrOptions 是计算属性接收函数方法
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
debugOptions?: DebuggerOptions,
isSSR = false
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// getterOrOptions 在这里进行判断的 typeof 是否是 function
const onlyGetter = isFunction(getterOrOptions)
if (onlyGetter) {
getter = getterOrOptions
// export const NOOP = () => {}
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
// 核心代码,实例化 ComputedRefImpl 对象
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
}
二、实际调用方法(被实例化的类):
*****首先需要明确一点,我们在创建一个 class 类的时候,constructor(构造函数)方法会创建和初始化class
创建的对象
初始化
constructor 的初始化过程:
第一步:调用了 ReactiveEffect
这个类,在第一次调用这个类的时候只会传入getter,之后会调用传入的第二个参数,第二个参数是计算属性的核心之处,在这里会判断是否需要重新计算。(在会面会分析ReactiveEffect
)
第二步:各种赋值操作(可见代码)
export class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
// readonly 是一个只读属性,这里也是个关键
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
) {
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
this.effect.computed = this
this.effect.active = this._cacheable = !isSSR
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
const self = toRaw(this)
// 这一步是为dep添加了 activeEffect
trackRefValue(self)
if (self._dirty || !self._cacheable) {
self._dirty = false
// 后面的感叹号是 非空断言操作符
self._value = self.effect.run()!
}
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
ReactiveEffect 类的调用:
初始化的时候调用方法 recordEffectScope(this, scope)
export class ReactiveEffect<T = any> {
active = true
deps: Dep[] = []
parent: ReactiveEffect | undefined = undefined
/**
* Can be attached after creation
* @internal
*/
computed?: ComputedRefImpl<T>
/**
* @internal
*/
allowRecurse?: boolean
/**
* @internal
*/
private deferStop?: boolean
onStop?: () => void
// dev only
onTrack?: (event: DebuggerEvent) => void
// dev only
onTrigger?: (event: DebuggerEvent) => void
constructor(
public fn: () => T,
public scheduler: EffectScheduler | null = null,
scope?: EffectScope
) {
// 主要执行这么一段代码 scope.effects.push(effect)
recordEffectScope(this, scope)
}
run() {
if (!this.active) {
return this.fn()
}
let parent: ReactiveEffect | undefined = activeEffect
let lastShouldTrack = shouldTrack
while (parent) {
// 在这里进行一个比较,如果上一次的缓存数据和当前一样不会再执行,直接返回
if (parent === this) {
return
}
parent = parent.parent
}
try {
this.parent = activeEffect
activeEffect = this
shouldTrack = true
trackOpBit = 1 << ++effectTrackDepth
if (effectTrackDepth <= maxMarkerBits) {
initDepMarkers(this)
} else {
cleanupEffect(this)
}
return this.fn()
} finally {
if (effectTrackDepth <= maxMarkerBits) {
finalizeDepMarkers(this)
}
trackOpBit = 1 << --effectTrackDepth
activeEffect = this.parent
shouldTrack = lastShouldTrack
this.parent = undefined
if (this.deferStop) {
this.stop()
}
}
}
stop() {
// stopped while running itself - defer the cleanup
if (activeEffect === this) {
this.deferStop = true
} else if (this.active) {
cleanupEffect(this)
if (this.onStop) {
this.onStop()
}
this.active = false
}
}
}
调用 recordEffectScope
方法:
在这一步会向 effects 中添加 effect
export function recordEffectScope(
effect: ReactiveEffect,
scope: EffectScope | undefined = activeEffectScope
) {
if (scope && scope.active) {
scope.effects.push(effect)
}
}
第一次调用 computed 方法执行过程:
在第一次调用的时候,会执行ComputedRefImpl
l类中的value()方法,但是首先会执行 constructor
的初始化,由于是第一次执行所以this._dirty是true,所以传入 ReactiveEffect
中的参数只有一个 ReactiveEffect(getter)。
详细分析这一步:
第一步:construct 初始化,this._dirty
为 true 所以传入的参数只有一个 ReactiveEffect(getter)。一定要记住这一步的 this._dirty ,在后需要他会起到是否执行计算计算方法的关键作用。
第二步:执行 value 方法
a:执行 trackRefValue() 方法,向 dep 中添加 activeEffect, 并向deps数组中添加 dep 代码:activeEffect!.deps.push(dep)
b:将 self._dirty 设置为 false,并调用实例化后的类this.effect执行它里面的run 方法最后返回 _value 值(下一步分析 run 方法)
export class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
// readonly 是一个只读属性
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
) {
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
this.effect.computed = this
this.effect.active = this._cacheable = !isSSR
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
const self = toRaw(this)
// 这一步是为dep添加了 activeEffect
trackRefValue(self)
if (self._dirty || !self._cacheable) {
self._dirty = false
// 后面的感叹号是 非空断言操作符
self._value = self.effect.run()!
}
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
执行run方法:
run() 方法是在类 ReactiveEffect 的方法(详细代码见上面的调用 recordEffectScope
方法),在这一步主要做的操作是将当前this赋值给 activeEffect ,在下一次执行的时候会判断上一次保存的 activeEffect 和当前的 this 是否相同,如果相同的话直接返回不再进行计算属性的执行。
run() {
if (!this.active) {
return this.fn()
}
let parent: ReactiveEffect | undefined = activeEffect
let lastShouldTrack = shouldTrack
while (parent) {
// 在这里进行一个比较,如果上一次的缓存数据和当前一样不会再执行,直接返回
if (parent === this) {
return
}
parent = parent.parent
}
try {
this.parent = activeEffect
activeEffect = this
shouldTrack = true
trackOpBit = 1 << ++effectTrackDepth
if (effectTrackDepth <= maxMarkerBits) {
initDepMarkers(this)
} else {
cleanupEffect(this)
}
return this.fn()
} finally {
if (effectTrackDepth <= maxMarkerBits) {
finalizeDepMarkers(this)
}
trackOpBit = 1 << --effectTrackDepth
activeEffect = this.parent
shouldTrack = lastShouldTrack
this.parent = undefined
if (this.deferStop) {
this.stop()
}
}
}
第二次以及之后调用 computed 方法执行过程:
继续探讨 ComputedRefImpl 类,由于是第二次调用,所以 ReactiveEffect 传入的是两个参数
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
// 在这里又将 this._dirty 设置为了 true
this._dirty = true
triggerRefValue(this)
}
})
分析一下,triggerRefValue() 方法做了什么
调用 triggerEffects(ref.dep) 方法
export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
ref = toRaw(ref)
if (ref.dep) {
if (__DEV__) {
triggerEffects(ref.dep, {
target: ref,
type: TriggerOpTypes.SET,
key: 'value',
newValue: newVal
})
} else {
// 这一步将 dep 解构且生成 effects 数组,首先判断是否具有这两个方法之后再调用 scheduler() 和 run() 方法
triggerEffects(ref.dep)
}
}
}
分析 triggerEffects(ref.dep) 方法:
这一步将 dep 解构且生成 effects 数组,首先判断是否具有这两个方法之后再调用 scheduler() 和 run() 方法,在这里是执行了 run 方法
export function triggerEffects(
dep: Dep | ReactiveEffect[],
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
// spread into array for stabilization
const effects = isArray(dep) ? dep : [...dep]
for (const effect of effects) {
if (effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo)
}
}
for (const effect of effects) {
if (!effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo)
}
}
}
function triggerEffect(
effect: ReactiveEffect,
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
if (effect !== activeEffect || effect.allowRecurse) {
if (__DEV__ && effect.onTrigger) {
effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
}
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
}
}
执行的run()方法和之前一样,就不在进行讨论了。