ReactiveEffect 作为 vue3 响应式对象中的订阅者,他可以订阅响应式对象的变化并做出对应的变化
ReactiveEffect 对象会在这几种场景下创建:
get
和 set
函数的对象来创建一个可写的 ref 对象。)注意:本文只分析 computed ReactiveEffect 的实现原理,由于 computed ReactiveEffect 比较长后文我会用ReactiveEffect
来替代computed ReactiveEffect
。
typescript
复制代码
`class ReactiveEffect<T = any> {
// 是否活跃
active = true
// dep 数组,在响应式对象收集依赖时也会将对应的依赖项添加到这个数组中
deps: Dep[] = []
// 上一个 ReactiveEffect 的实例
parent: ReactiveEffect | undefined = undefined
// 创建后可能会附加的属性,如果是 computed 则指向 ComputedRefImpl
computed?: ComputedRefImpl<T>
// 是否允许递归,会被外部更改,
allowRecurse?: boolean
// 延迟停止
private deferStop?: boolean
// 停止事件
onStop?: () => void
// dev only
onTrack?: (event: DebuggerEvent) => void
// dev only
onTrigger?: (event: DebuggerEvent) => void
constructor(
// 参数赋值给fn
public fn: () => T,
// 参数赋值给 scheduler
public scheduler: EffectScheduler | null = null,
scope?: EffectScope
) {
// 标记作用域
recordEffectScope(this, scope)
}
run() {
if (!this.active) {
return this.fn()
}
// 存储最上层 ReactiveEffect 对象
let parent: ReactiveEffect | undefined = activeEffect
// 缓存 是否可以跟踪依赖 上一次的结果
let lastShouldTrack = shouldTrack
while (parent) {
if (parent === this) {
return
}
parent = parent.parent
}
try {
// 父结点指向上一个 ReactiveEffect
this.parent = activeEffect
// 当前活跃的 ReactiveEffect
activeEffect = this
// 允许追踪依赖
shouldTrack = true
// 定义当前的 ReactiveEffect 层级
trackOpBit = 1 << ++effectTrackDepth
// 当前层级没超过最大层级限制
if (effectTrackDepth <= maxMarkerBits) {
// 初始化 ReactiveEffect 对应的 Dep 集合 标记
initDepMarkers(this)
} else {
// 清除副作用,一般不会触发
cleanupEffect(this)
}
// 执行构造函数传入的方法
return this.fn()
} finally {
// 当前层级没超过最大层级限制,清空标记
if (effectTrackDepth <= maxMarkerBits) {
finalizeDepMarkers(this)
}
// 回到上一层 ReactiveEffect
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
}
}
}`
上面代码注释对 ReactiveEffect 的变量做了一些说明,我们再介绍下其中比较关键的变量:
trackOpBit: 一个二进制变量,表示当前 ReactivEffect 的层级。
effectTrackDepth: 表示 trackOpBit 右移的位数。
activeEffect 当前活跃的副作用对象。
typescript
复制代码
`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
setter = NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)
return cRef as any
}
export class ComputedRefImpl<T> {
// dep集合,用来存储依赖向 ReactiveEffect
public dep?: Dep = undefined
// computed 值的引用
private _value!: T
// computed 内部创建的 ReactiveEffect 对象。
public readonly effect: ReactiveEffect<T>
// 标记
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean
public _dirty = true
// 默认为 true 不讨论服务端渲染场景
public _cacheable: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean,
isSSR: boolean
) {
// 创建 ReactiveEffect 实例
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
// 设置内部创建的 ReactiveEffect 对象 computed 属性
this.effect.computed = this
// 我们不讨论服务端渲染,这里为true
this.effect.active = this._cacheable = !isSSR
// 不显示传递 setter 就是只读
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
// 获取原始对象
const self = toRaw(this)
// 收集依赖
trackRefValue(self)
// 首次触发get,会进入这个判断,并调用 ReactiveEffect 对象的 run 方法。
if (self._dirty || !self._cacheable) {
self._dirty = false
self._value = self.effect.run()!
}
// 返回对应的值
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}`
从上面代码可以看出,computed 核心在于 class ComputedRefImpl,下面开始分析这个 class
ComputedRefImpl 内部结构:
属性
构造函数:
get value
vue
复制代码
`<template>
<h1>{{ b }}</h1>
<button @click="onClick">按钮</button>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
const a = ref(1);
const b = computed(() => a.value);
const onClick = () => {
a.value++;
};
</script>`
简单介绍一下上面代码的关键逻辑:
我们从初始化,点击按钮,重新渲染这三个阶段来分析 ComputedRefImpl 的内部变化:
初始化:
点击按钮:
重新渲染:
依赖收集有去重的逻辑,因为去重逻辑不影响我们分析,所以我在上面忽略了这一过程
ReactiveEffect 与响应式对象的交同上面三个阶段的描述所示,这个过程逻辑也是非常清晰的,关于 computed ReactiveEffect 分析就到这儿了,如果我在文中的表述有不恰当或者不准确的地方,请各位掘友不吝赐教。
原网址: 访问
创建于: 2023-10-12 15:39:36
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论