记录一下 effect 的理解
首先看下effect.js
中,都导出了什么? 除了接口和类型,文件由上至下分别导出
export {
effect, // 副作用函数
stop, // 停止
pauseTracking, // 暂停跟踪
enableTracking, // 启用跟踪
resetTracking, // 重置跟踪
track, // 设置映射关系
trigger, // 派发更新
} from './effect'
复制代码
现在就来研究一下每个方法。
effect
// 涉及到的类型定义 or 方法
export function isEffect(fn: any): fn is ReactiveEffect {
return fn && fn._isEffect === true
}
export interface ReactiveEffectOptions {
lazy?: boolean
scheduler?: (job: ReactiveEffect) => void
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
onStop?: () => void
allowRecurse?: boolean
}
// 正文
export function effect<T = any>(
fn: () => T,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
// 是否已经处理过得函数
// 不理解可以先略过,回头再看
if (isEffect(fn)) {
fn = fn.raw
}
// `变异函数`
const effect = createReactiveEffect(fn, options)
// lazy == falsy [假值]
if (!options.lazy) {
effect()
}
return effect
}
复制代码
可以看出来 effect 函数,主要是 return createReactiveEffect
方法的返回值,并判断是否需要立即执行。
createReactiveEffect
// 涉及到的类型定义 or 方法
const effectStack: ReactiveEffect[] = []; // effect 存储
export interface ReactiveEffect<T = any> {
(): T
_isEffect: true
id: number
active: boolean
raw: () => T
deps: Array<Dep>
options: ReactiveEffectOptions
allowRecurse: boolean
}
// 正文
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
// 定义一个函数
const effect = function reactiveEffect(): unknown {
// 当这个函数执行时!
if (!effect.active) {
return options.scheduler ? undefined : fn()
}
// 当 effect 没有被 effectStack 收集时
if (!effectStack.includes(effect)) {
cleanup(effect) // 清理 deps
try {
enableTracking() // 启用追踪
effectStack.push(effect) // 收集
activeEffect = effect // 赋值
return fn() // 执行函数
} finally {
effectStack.pop() // 删除
resetTracking() // 停止追踪
activeEffect = effectStack[effectStack.length - 1] // 赋值
}
}
} as ReactiveEffect
// 函数上增加一些属性, 注意下 `_isEffect` (再看回上文)
effect.id = uid++
effect.allowRecurse = !!options.allowRecurse
effect._isEffect = true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect // return 这个变异的函数
}
// 清理 deps
function cleanup(effect: ReactiveEffect) {
const { deps } = effect
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect)
}
deps.length = 0
}
}
复制代码
这里可以看出来,createReactiveEffect
的函数,主要是返回一个经过处理后的变异 effect
函数, 并立即执行此函数。 那这个被赋值的 activeEffect
是什么呢?
activeEffect
export interface ReactiveEffect<T = any> {
(): T
_isEffect: true
id: number
active: boolean
raw: () => T
deps: Array<Dep>
options: ReactiveEffectOptions
allowRecurse: boolean
}
let activeEffect: ReactiveEffect | undefined
复制代码
可以看出就是一个变异函数,这里要记一下。
track
接下来看一下 track
– 追踪函数。
let shouldTrack = true
// 下面的可以看成映射
type Dep = Set<ReactiveEffect>
type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<any, KeyToDepMap>()
export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) {
return
}
// 下面就是 设置映射关系 重要的就是这里
// A. 获取,获取 target 的 映射 WeakMap -> Map
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// B. 通过上面的 Map ,与入参的 key, 作第二次映射 Map -> Set
// dep 就是映射到最后的 value
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// 这里判断 dep 是否已经设置映射的值
if (!dep.has(activeEffect)) {
// C. 设置映射的值 `activeEffect`
dep.add(activeEffect)
// 这里 deps 中添加 dep
activeEffect.deps.push(dep)
// 如果开发环境 并且 options.onTrack 存在,执行
if (__DEV__ && activeEffect.options.onTrack) {
activeEffect.options.onTrack({
effect: activeEffect,
target,
type,
key
})
}
}
}
复制代码
这里主要是映射的关系 {target -> key -> dep} -> {WeakMap -> Map -> Set}
,也就是收集
trigger
接下来看一下 trigger
超级长的一串代码
// 类型定义
export const enum TriggerOpTypes {
SET = 'set',
ADD = 'add',
DELETE = 'delete',
CLEAR = 'clear'
}
export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '')
export function trigger(
target: object, // 目标
type: TriggerOpTypes, // 类型
key?: unknown, // key
newValue?: unknown, // 新值
oldValue?: unknown, // 旧值
oldTarget?: Map<unknown, unknown> | Set<unknown> // 旧目标
) {
// 获取 target 映射的 Map
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
// 这里很重要,effects 用来收集
const effects = new Set<ReactiveEffect>()
// 收集的方法
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
// 下面的可以理解成,在各种情况下, 根据 `track` 收集到的依赖映射
// 也就是 `{target -> key -> dep} {WeakMap -> Map -> Set}` 中的 Set -> dep
// 根据 depsMap.get() 找到,并通过 add 方法 收集到 effects 中
// A. 清除 - 遍历 Map
if (type === TriggerOpTypes.CLEAR) {
depsMap.forEach(add)
// B. key = `length` && target 是一个数组
} else if (key === 'length' && isArray(target)) {
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= (newValue as number)) {
add(dep)
}
})
} else {
// C. key !== undefined
if (key !== void 0) {
add(depsMap.get(key))
}
// D. switch -> 以下add 的入参都是 Set -> [activeEffect]
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
add(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
add(depsMap.get(MAP_KEY_ITERATE_KEY))
}
// 是整数键
} else if (isIntegerKey(key)) {
add(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
add(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
add(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
add(depsMap.get(ITERATE_KEY))
}
break
}
}
// 触发方法 - 执行其中的函数来派发更新
const run = (effect: ReactiveEffect) => {
if (__DEV__ && effect.options.onTrigger) {
effect.options.onTrigger({
effect,
target,
key,
type,
newValue,
oldValue,
oldTarget
})
}
if (effect.options.scheduler) {
effect.options.scheduler(effect)
} else {
effect()
}
}
// 遍历执行变异函数 `activeEffect`
effects.forEach(run)
}
复制代码
可以看到 trigger
函数,主要就是通过映射关系,执行变异函数
shouldTrack
这里还有一段关于 shouldTrack 的函数,主要是起到开关作用。
let shouldTrack = true
const trackStack: boolean[] = []
export function pauseTracking() {
trackStack.push(shouldTrack)
shouldTrack = false
}
export function enableTracking() {
trackStack.push(shouldTrack)
shouldTrack = true
}
export function resetTracking() {
const last = trackStack.pop()
shouldTrack = last === undefined ? true : last
}
复制代码
就到这里,转载请声明出处。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END