源码版本:3.2.37
普通的🌰
访问时添加依赖,更新时重新运行副作用函数
/* 响应式对象1 */
const r1 = ref(0)
/* 响应式对象2 */
const r2 = reactive({ p: 0 })
/* 创建 ReactiveEffect实例,并运行实例 run 方法 */
effect(() => {
/* 访问对象,分别调用 trackRefValue 和 track */
console.log(r1.value, r2.p) // 打印 0 0
/* 调用 trigger,这里不会触发副作用函数运行 */
r1.value++
})
/* 调用 triggerRefValue */
r1.value++ // 打印 2 0
/* 调用 trigger */
r2.p++ // 打印 3 1
Dep:双方的桥梁
源码相对路径:packages/reactivity/src/dep.ts
dep对象,用于存储 ReactiveEffect实例,是响应式对象必备值,当响应式对象属性值发生变化时,就会遍历 dep对象 通知 ReactiveEffect实例 执行相关方法,实现“响应式更新”
创建对象
调用 createDep
生成 Set实例,并且在该实例设置 w
和 n
两个数值类型属性:
w
表示 ReactiveEffect实例 已经追踪过该响应式对象的属性,ReactiveEffect实例 在执行副作用函数前对其赋值n
表示 ReactiveEffect实例 在此次执行副作用函数时 首次访问该响应式对象的属性,在副作用函数访问响应式对象属性时,相关函数赋值
export const createDep = (effects?: ReactiveEffect[]): Dep => {
const dep = new Set<ReactiveEffect>(effects) as Dep
dep.w = 0
dep.n = 0
return dep
}
辅助函数
辅助函数都绕不开 trackOpBit
, 它是 effect.ts 代码模块里的变量,用于区分在 调用栈中 各个 ReactiveEffect实例 运行的副作用函数
wasTracked
和 newTracked
函数用来判断当前
export const wasTracked = (dep: Dep): boolean => (dep.w & trackOpBit) > 0
export const newTracked = (dep: Dep): boolean => (dep.n & trackOpBit) > 0
initDepMarkers
在 ReactiveEffect实例 运行副作用函数 前 执行,用于对 ReactiveEffect实例 的所有依赖 dep对象 的 w
属性赋值,表示响应式对象的属性追踪过
export const initDepMarkers = ({ deps }: ReactiveEffect) => {
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
/* 标记 */
deps[i].w |= trackOpBit
}
}
}
finalizeDepMarkers
在 ReactiveEffect实例 运行副作用函数 后 执行,清除 ReactiveEffect实例 所有 dep对象 关于此 trackOpBit
export const finalizeDepMarkers = (effect: ReactiveEffect) => {
const { deps } = effect
if (deps.length) {
let ptr = 0
for (let i = 0; i < deps.length; i++) {
const dep = deps[i]
if (wasTracked(dep) && !newTracked(dep)) {
/* 存在但未能访问到,删除多余数据 */
dep.delete(effect)
} else {
deps[ptr++] = dep
}
/* 清除标记 */
dep.w &= ~trackOpBit
dep.n &= ~trackOpBit
}
deps.length = ptr
}
}
ReactiveEffect
源码相对路径:packages/reactivity/src/effect.ts
effect函数
该函数接收副作用函数,并用其创建 ReactiveEffect实例,最后返回实例的 run 方法
export function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions
): ReactiveEffectRunner {
/* 如果是 ReactiveEffect实例 的 run 方法时,获取真正的副作用函数 */
if ((fn as ReactiveEffectRunner).effect) {
fn = (fn as ReactiveEffectRunner).effect.fn
}
/* 创建实例 */
const _effect = new ReactiveEffect(fn)
/* 自定义设置 */
if (options) {
extend(_effect, options)
if (options.scope) recordEffectScope(_effect, options.scope)
}
/* 可设置懒执行 */
if (!options || !options.lazy) {
_effect.run()
}
/* ReactiveEffect实例 的 run 方法 */
const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
runner.effect = _effect
return runner
}
ReactiveEffect类
该类只需着重查看 run 方法即可
export class ReactiveEffect<T = any> {
active = true
deps: Dep[] = []
parent: ReactiveEffect | undefined = undefined
computed?: ComputedRefImpl<T> /* computed 的 ReactiveEffect实例 */
allowRecurse?: boolean
private deferStop?: boolean
onStop?: () => void
// dev only
constructor(
public fn: () => T,/* 副作用函数 */
public scheduler: EffectScheduler | null = null,/* 调度任务 */
scope?: EffectScope
) {
/* scope 用于统一关闭相关 ReactiveEffect实例 的响应式操作 */
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
/* 当前 ReactiveEffect实例 占用位 */
trackOpBit = 1 << ++effectTrackDepth
if (effectTrackDepth <= maxMarkerBits) {
/* 标记 dep对象的 w 属性 */
initDepMarkers(this)
} else {
/* 旧模式,初始化 deps 属性,即清空双方关系 */
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() {
if (activeEffect === this) {
/* 在副作用函数运行中调用 stop,推迟执行 */
this.deferStop = true
} else if (this.active) {
/* 清空依赖 */
cleanupEffect(this)
/* 执行监听函数 */
if (this.onStop) {
this.onStop()
}
/* 标记其不参与响应式操作 */
this.active = false
}
}
}
/* 解除双方关系 */
function cleanupEffect(effect: ReactiveEffect) {
const { deps } = effect
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect)
}
deps.length = 0
}
}
track函数
该函数的目的是获取对象属性的 dep对象,最后调用 trackEffects
。在访问响应式对象属性时,将调用 track
;ref类型对象使用 trackRefValue
export function track(target: object, type: TrackOpTypes, key: unknown) {
/* 检查当前 ReactiveEffect实例 能否追踪,是否存在实例对象 */
if (shouldTrack && activeEffect) {
/* 获取 target 保存的 map对象 */
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
/* 获取对应属性名的 dep对象 */
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = createDep()))
}
// 删除dev流程,eventInfo = undefined
trackEffects(dep, eventInfo)
}
}
// ref类型对象使用此函数
export function trackRefValue(ref: RefBase<any>) {
if (shouldTrack && activeEffect) {
ref = toRaw(ref)
// 删除dev流程
trackEffects(ref.dep || (ref.dep = createDep()))
}
}
trackEffects函数
调用该函数添加 ReactiveEffect实例,ReactiveEffect实例 同样添加 dep对象
export function trackEffects(
dep: Dep,
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
/* 是否添加当前运行的 ReactiveEffect实例 */
let shouldTrack = false
if (effectTrackDepth <= maxMarkerBits) {/* 位操作模式,优化性能 */
/* 只在副作用函数第一次访问时操作 */
if (!newTracked(dep)) {
/* 标记 n 属性,声明访问过 */
dep.n |= trackOpBit
shouldTrack = !wasTracked(dep)
}
} else {
/* 旧模式 */
shouldTrack = !dep.has(activeEffect!)
}
if (shouldTrack) {/* 互相添加依赖 */
dep.add(activeEffect!)
activeEffect!.deps.push(dep)
// 删除dev流程
}
}
trigger函数
该函数的目的是获取对象属性的 dep对象,最后调用 triggerEffects
。在设置响应式对象属性值时,将调用 trigger
;ref类型对象使用 triggerRefValue
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
if (!depsMap) {/* 无相关依赖 */
return
}
let deps: (Dep | undefined)[] = []
// 根据数据类型和操作类型获取相关 dep对象,填充 deps。具体可查看源码
// 删除dev流程
if (deps.length === 1) {
if (deps[0]) {
// 删除dev流程
triggerEffects(deps[0])
}
} else {
const effects: ReactiveEffect[] = []
for (const dep of deps) {
if (dep) {
effects.push(...dep)
}
}
// 删除dev流程
/* triggerEffects 接收的是 dep对象,但 deps 是数组,所以需要创建一个 */
triggerEffects(createDep(effects))
}
}
// ref类型对象使用此函数
export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
ref = toRaw(ref)
if (ref.dep) {
// 删除dev流程
triggerEffects(ref.dep)
}
}
triggerEffects函数
该函数执行所有 dep对象 里的副作用函数,优先刷新 computed对象 的副作用函数(解决bug);优先执行 scheduler 任务
export function triggerEffects(
dep: Dep | ReactiveEffect[],
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
const effects = isArray(dep) ? dep : [...dep]
/* 优先执行 computed对象 的 ReactiveEffect实例 任务 */
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
) {
/* 第一个条件:处理在副作用函数中设置响应式对象属性值的情况 */
/* 第二个条件:
参考 https://github.com/vuejs/core/issues/1801
https://github.com/vuejs/core/issues/2043
*/
if (effect !== activeEffect || effect.allowRecurse) {
// 删除dev流程
/* 优先执行 scheduler 任务 */
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
}
}