Vue3?源碼解讀之副作用函數(shù)與依賴收集
版本:3.2.31
副作用函數(shù)
副作用函數(shù)是指會產(chǎn)生副作用的函數(shù),如下面的代碼所示:
function effect(){
document.body.innerText = 'hello vue3'
}當(dāng) effect 函數(shù)執(zhí)行時,它會設(shè)置 body 的文本內(nèi)容,但除了 effect 函數(shù)之外的任何函數(shù)都可以讀取或設(shè)置 body 的文本內(nèi)容。也就是說,effect 函數(shù)的執(zhí)行會直接或間接影響其他函數(shù)的執(zhí)行,這時我們說 effect 函數(shù)產(chǎn)生了副作用。副作用很容易產(chǎn)生,例如一個函數(shù)修改了全局變量,這其實也是一個副作用。
// 全局變量
let val = 1
function effect() {
val = 2 // 修改全局變量,產(chǎn)生副作用
}副作用函數(shù)的全局變量
在副作用模塊中,定義了幾個全局的變量,提前認(rèn)識這些變量有助與我們了解副作用函數(shù)的生成以及調(diào)用的過程。
// packages/reactivity/src/effect.ts export type Dep = Set<ReactiveEffect> & TrackedMarkers type KeyToDepMap = Map<any, Dep> // WeakMap 集合存儲副作用函數(shù) const targetMap = new WeakMap<any, KeyToDepMap>() // 用一個全局變量存儲當(dāng)前激活的 effect 函數(shù) export let activeEffect: ReactiveEffect | undefined // 標(biāo)識是否開啟了依賴收集 export let shouldTrack = true const trackStack: boolean[] = []
targetMap
targetMap 是一個 WeakMap 類型的集合,用來存儲副作用函數(shù),從類型定義可以看出 targetMap的數(shù)據(jù)結(jié)構(gòu)方式:
- WeakMap 由
target --> Map構(gòu)成 - Map 由
key --> Set構(gòu)成
其中 WeakMap 的鍵是原始對象 target,WeakMap 的值是一個 Map 實例,Map 的鍵是原始對象 target 的 key,Map 的值是一個由副作用函數(shù)組成的 Set。它們的關(guān)系如下:

targetMap 為什么使用 WeakMap
我們來看下面的代碼:
const map = new Map();
const weakMap = new WeakMap();
(function() {
const foo = {foo: 1};
const bar = {bar: 2};
map.set(foo, 1); // foo 對象是 map 的key
weakMap.set(bar, 2); // bar 對象是 weakMap 的 key
})在上面的代碼中,定義了 map 和 weakMap 常量,分別對應(yīng) Map 和 WeakMap 的實例。在立即執(zhí)行的函數(shù)表達(dá)式內(nèi)部定義了兩個對象:foo 和 bar,這兩個對象分別作為 map 和 weakMap 的key。
當(dāng)函數(shù)表達(dá)式執(zhí)行完畢后,對于對象 foo 來說,它仍然作為 map 的 key 被引用著,因此垃圾回收器不會把它從內(nèi)存中移除,我們?nèi)匀豢梢酝ㄟ^ map.keys 打印出對象 foo 。
對于對象 bar 來說,由于 WeakMap 的 key 是弱引用,它不影響垃圾收集器的工作,所以一旦表達(dá)式執(zhí)行完畢,垃圾回收器就會把對象 bar 從內(nèi)存中移除,并且我們無法獲取 weakMap 的 key 值,也就無法通過 weakMap 取得對象 bar 。
簡單地說,WeakMap 對 key 是弱引用,不影響垃圾回收器的工作**。根據(jù)這個特性可知,一旦 key 被垃圾回收器回收,那么對應(yīng)的鍵和值就訪問不到了。所以 WeakMap 經(jīng)常用于存儲那些只有當(dāng) key 所引用的對象存在時 (沒有被回收) 才有價值的信息**。
例如在上面的場景中,如果 target 對象沒有任何引用了,說明用戶側(cè)不再需要它了,這時垃圾回收器會完成回收任務(wù)。但如果使用 Map 來代替 WeakMap,那么即使用戶側(cè)的代碼對 target 沒有任何引用,這個 target 也不會被回收,最終可能導(dǎo)致內(nèi)存溢出。
activeEffect
activeEffect 變量用來維護(hù)當(dāng)前正在執(zhí)行的副作用
shouldTrack
shouldTrack 變量用來標(biāo)識是否開啟依賴搜集,只有 shouldTrack 的值為 true 時,才進(jìn)行依賴收集,即將副作用函數(shù)添加到依賴集合中。
副作用的實現(xiàn)
effect 函數(shù)
effect API 用來創(chuàng)建一個副作用函數(shù),接受兩個參數(shù),分別是用戶自定義的fn函數(shù)和options 選項。源碼如下所示:
// packages/reactivity/src/effect.ts
export function effect<T = any>(
fn: () => T,
options?: ReactiveEffectOptions
): ReactiveEffectRunner {
// 當(dāng)傳入的 fn 中存在 effect 副作用時,將這個副作用的原始函數(shù)賦值給 fn
if ((fn as ReactiveEffectRunner).effect) {
fn = (fn as ReactiveEffectRunner).effect.fn
}
// 創(chuàng)建一個副作用
const _effect = new ReactiveEffect(fn)
if (options) {
extend(_effect, options)
if (options.scope) recordEffectScope(_effect, options.scope)
}
// 如果不是延遲執(zhí)行的,則立即執(zhí)行一次副作用函數(shù)
if (!options || !options.lazy) {
_effect.run()
}
// 通過 bind 函數(shù)返回一個新的副作用函數(shù)
const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
// 將副作用添加到新的副作用函數(shù)上
runner.effect = _effect
// 返回這個新的副作用函數(shù)
return runner
}由上面的代碼可以知道,當(dāng)傳入的參數(shù) fn 中存在 effect 副作用時,將這個副作用的原始函數(shù)賦值給 fn。然后調(diào)用 ReactiveEffect 類創(chuàng)建一個封裝后的副作用函數(shù)。
在有些場景下,我們不希望 effect 立即執(zhí)行,而是希望它在需要的時候才執(zhí)行,我們可以通過在 options 中添加 lazy 屬性來達(dá)到目的。在 effect 函數(shù)源碼中,判斷 options.lazy 選項的值,當(dāng)值為true 時,則不立即執(zhí)行副作用函數(shù),從而實現(xiàn)懶執(zhí)行的 effect。
接著通過 bind 函數(shù)返回一個新的副作用函數(shù)runner,這個新函數(shù)的this被指定為 _effect,并將 _effect 添加到這個新副作用函數(shù)的 effect 屬性上,最后返回這個新副作用函數(shù)。
由于 effect API 返回的是封裝后的副作用函數(shù),原始的副作用函數(shù)存儲在封裝后的副作用函數(shù)的effect屬性上,因此如果想要獲取用戶傳入的副作用函數(shù),需要通過 fn.effect.fn 來獲取。
在 effect 函數(shù)中調(diào)用了 ReactiveEffect 類創(chuàng)建副作用,接下來看看 ReactiveEffect 類的實現(xiàn)。
ReactiveEffect 類
// packages/reactivity/src/effect.ts
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
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
) {
recordEffectScope(this, scope)
}
run() {
// 如果 effect 已停用,返回原始副作用函數(shù)執(zhí)行后的結(jié)果
if (!this.active) {
return this.fn()
}
let parent: ReactiveEffect | undefined = activeEffect
let lastShouldTrack = shouldTrack
while (parent) {
if (parent === this) {
return
}
parent = parent.parent
}
try {
// 創(chuàng)建一個新的副作用前將當(dāng)前正在執(zhí)行的副作用存儲到新建的副作用的 parent 屬性上,解決嵌套effect 的情況
this.parent = activeEffect
// 將創(chuàng)建的副作用設(shè)置為當(dāng)前正則正在執(zhí)行的副作用
activeEffect = this
// 將 shouldTrack 設(shè)置為 true,表示開啟依賴收集
shouldTrack = true
trackOpBit = 1 << ++effectTrackDepth
if (effectTrackDepth <= maxMarkerBits) {
// 初始化依賴
initDepMarkers(this)
} else {
// 清除依賴
cleanupEffect(this)
}
// 返回原始副作用函數(shù)執(zhí)行后的結(jié)果
return this.fn()
} finally {
if (effectTrackDepth <= maxMarkerBits) {
finalizeDepMarkers(this)
}
trackOpBit = 1 << --effectTrackDepth
// 重置當(dāng)前正在執(zhí)行的副作用
activeEffect = this.parent
shouldTrack = lastShouldTrack
this.parent = undefined
}
}
// 停止(清除) effect
stop() {
if (this.active) {
cleanupEffect(this)
if (this.onStop) {
this.onStop()
}
this.active = false
}
}
}
在 ReactiveEffect 類中,定義了一個 run 方法,這個 run 方法就是創(chuàng)建副作用時實際運行方法。每次派發(fā)更新時,都會執(zhí)行這個run方法,從而更新值。
全局變量 activeEffect 用來維護(hù)當(dāng)前正在執(zhí)行的副作用,當(dāng)存在嵌套渲染組件的時候,依賴收集后,副作用函數(shù)會被覆蓋,即 activeEffect 存儲的副作用函數(shù)在嵌套 effect 的時候會被內(nèi)層的副作用函數(shù)覆蓋。為了解決這個問題,在 run 方法中,將當(dāng)前正在執(zhí)行的副作用activeEffect保存到新建的副作用的 parent 屬性上,然后再將新建的副作用設(shè)置為當(dāng)前正在執(zhí)行的副作用。在新建的副作用執(zhí)行完畢后,再將存儲到 parent 屬性的副作用重新設(shè)置為當(dāng)前正在執(zhí)行的副作用。
在 ReactiveEffect 類中,還定義了一個 stop 方法,該方法用來停止并清除當(dāng)前正在執(zhí)行的副作用。
track 收集依賴
當(dāng)使用代理對象訪問對象的屬性時,就會觸發(fā)代理對象的 get 攔截函數(shù)執(zhí)行,如下面的代碼所示:
const obj = { foo: 1 }
const p = new Proxy(obj, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
}
})
p.foo在上面的代碼中,通過代理對象p 訪問 foo 屬性,便會觸發(fā) get 攔截函數(shù)的執(zhí)行,此時就在 get 攔截函數(shù)中調(diào)用 track 函數(shù)進(jìn)行依賴收集。源碼中 get 攔截函數(shù)的解析可閱讀《Vue3 源碼解讀之非原始值的響應(yīng)式原理》一文中的「訪問屬性的攔截」小節(jié)。
下面,我們來看看 track 函數(shù)的實現(xiàn)。
track 函數(shù)
// packages/reactivity/src/effect.ts
// 收集依賴
export function track(target: object, type: TrackOpTypes, key: unknown) {
// 如果開啟了依賴收集并且有正在執(zhí)行的副作用,則收集依賴
if (shouldTrack && activeEffect) {
// 在 targetMap 中獲取對應(yīng)的 target 的依賴集合
let depsMap = targetMap.get(target)
if (!depsMap) {
// 如果 target 不在 targetMap 中,則加入,并初始化 value 為 new Map()
targetMap.set(target, (depsMap = new Map()))
}
// 從依賴集合中獲取對應(yīng)的 key 的依賴
let dep = depsMap.get(key)
if (!dep) {
// 如果 key 不存在,將這個 key 作為依賴收集起來,并初始化 value 為 new Set()
depsMap.set(key, (dep = createDep()))
}
const eventInfo = __DEV__
? { effect: activeEffect, target, type, key }
: undefined
trackEffects(dep, eventInfo)
}
}在 track 函數(shù)中,通過一個 if 語句判斷是否進(jìn)行依賴收集,只有當(dāng) shouldTrack 為 true 并且存在 activeEffect,即開啟了依賴收集并且存在正在執(zhí)行的副作用時,才進(jìn)行依賴收集。
然后通過 target 對象從 targetMap 中嘗試獲取對應(yīng) target 的依賴集合depsMap,如果 targetMap 中不存在當(dāng)前target的依賴集合,則將當(dāng)前 target 添加進(jìn) targetMap 中,并將 targetMap 的 value 初始化為 new Map()。
// 在 targetMap 中獲取對應(yīng)的 target 的依賴集合
let depsMap = targetMap.get(target)
if (!depsMap) {
// 如果 target 不在 targetMap 中,則加入,并初始化 value 為 new Map()
targetMap.set(target, (depsMap = new Map()))
}接著根據(jù)target中被讀取的 key,從依賴集合depsMap中獲取對應(yīng) key 的依賴,如果依賴不存在,則將這個 key 的依賴收集到依賴集合depsMap中,并將依賴初始化為 new Set()。
// 從依賴集合中獲取對應(yīng)的 key 的依賴
let dep = depsMap.get(key)
if (!dep) {
// 如果 key 不存在,將這個 key 作為依賴收集起來,并初始化 value 為 new Set()
depsMap.set(key, (dep = createDep()))
}最后調(diào)用 trackEffects 函數(shù),將副作用函數(shù)收集到依賴集合depsMap中。
const eventInfo = __DEV__
? { effect: activeEffect, target, type, key }
: undefined
trackEffects(dep, eventInfo)trackEffects 函數(shù)
// 收集副作用函數(shù)
export function trackEffects(
dep: Dep,
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
let shouldTrack = false
if (effectTrackDepth <= maxMarkerBits) {
if (!newTracked(dep)) {
dep.n |= trackOpBit // set newly tracked
shouldTrack = !wasTracked(dep)
}
} else {
// Full cleanup mode.
// 如果依賴中并不存當(dāng)前的 effect 副作用函數(shù)
shouldTrack = !dep.has(activeEffect!)
}
if (shouldTrack) {
// 將當(dāng)前的副作用函數(shù)收集進(jìn)依賴中
dep.add(activeEffect!)
// 并在當(dāng)前副作用函數(shù)的 deps 屬性中記錄該依賴
activeEffect!.deps.push(dep)
if (__DEV__ && activeEffect!.onTrack) {
activeEffect!.onTrack(
Object.assign(
{
effect: activeEffect!
},
debuggerEventExtraInfo
)
)
}
}
}
在 trackEffects 函數(shù)中,檢查當(dāng)前正在執(zhí)行的副作用函數(shù) activeEffect 是否已經(jīng)被收集到依賴集合中,如果沒有,就將當(dāng)前的副作用函數(shù)收集到依賴集合中。同時在當(dāng)前副作用函數(shù)的 deps 屬性中記錄該依賴。
trigger 派發(fā)更新
當(dāng)對屬性進(jìn)行賦值時,會觸發(fā)代理對象的 set 攔截函數(shù)執(zhí)行,如下面的代碼所示:
const obj = { foo: 1 }
const p = new Proxy(obj, {
// 攔截設(shè)置操作
set(target, key, newVal, receiver){
// 如果屬性不存在,則說明是在添加新屬性,否則設(shè)置已有屬性
const type = Object.prototype.hasOwnProperty.call(target,key) ? 'SET' : 'ADD'
// 設(shè)置屬性值
const res = Reflect.set(target, key, newVal, receiver)
// 把副作用函數(shù)從桶里取出并執(zhí)行,將 type 作為第三個參數(shù)傳遞給 trigger 函數(shù)
trigger(target,key,type)
return res
}
// 省略其他攔截函數(shù)
})
p.foo = 2在上面的代碼中,通過代理對象p 訪問 foo 屬性,便會觸發(fā) set 攔截函數(shù)的執(zhí)行,此時就在 set 攔截函數(shù)中調(diào)用 trigger 函數(shù)中派發(fā)更新。源碼中 set 攔截函數(shù)的解析可閱讀《Vue3 源碼解讀之非原始值的響應(yīng)式原理》一文中的「設(shè)置屬性操作的攔截」小節(jié)。
下面,我們來看看 track 函數(shù)的實現(xiàn)。
trigger 函數(shù)
trigger 函數(shù)的源碼如下:
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
// 該 target 從未被追蹤,則不繼續(xù)執(zhí)行
if (!depsMap) {
// never been tracked
return
}
// 存放所有需要派發(fā)更新的副作用函數(shù)
let deps: (Dep | undefined)[] = []
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
// 當(dāng)需要清除依賴時,將當(dāng)前 target 的依賴全部傳入
deps = [...depsMap.values()]
} else if (key === 'length' && isArray(target)) {
// 處理數(shù)組的特殊情況
depsMap.forEach((dep, key) => {
// 如果對應(yīng)的長度, 有依賴收集需要更新
if (key === 'length' || key >= (newValue as number)) {
deps.push(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
// 在 SET | ADD | DELETE 的情況,添加當(dāng)前 key 的依賴
if (key !== void 0) {
deps.push(depsMap.get(key))
}
// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
// 操作類型為 ADD 時觸發(fā)Map 數(shù)據(jù)結(jié)構(gòu)的 keys 方法的副作用函數(shù)重新執(zhí)行
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// new index added to array -> length changes
deps.push(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
// 操作類型為 DELETE 時觸發(fā)Map 數(shù)據(jù)結(jié)構(gòu)的 keys 方法的副作用函數(shù)重新執(zhí)行
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
deps.push(depsMap.get(ITERATE_KEY))
}
break
}
}
const eventInfo = __DEV__
? { target, type, key, newValue, oldValue, oldTarget }
: undefined
if (deps.length === 1) {
if (deps[0]) {
if (__DEV__) {
triggerEffects(deps[0], eventInfo)
} else {
triggerEffects(deps[0])
}
}
} else {
const effects: ReactiveEffect[] = []
// 將需要執(zhí)行的副作用函數(shù)收集到 effects 數(shù)組中
for (const dep of deps) {
if (dep) {
effects.push(...dep)
}
}
if (__DEV__) {
triggerEffects(createDep(effects), eventInfo)
} else {
triggerEffects(createDep(effects))
}
}
}
在 trigger 函數(shù)中,首先檢查當(dāng)前 target 是否有被追蹤,如果從未被追蹤過,即target的依賴未被收集,則不需要執(zhí)行派發(fā)更新,直接返回即可。
const depsMap = targetMap.get(target)
// 該 target 從未被追蹤,則不繼續(xù)執(zhí)行
if (!depsMap) {
// never been tracked
return
}接著創(chuàng)建一個 Set 類型的 deps 集合,用來存儲當(dāng)前target的這個 key 所有需要執(zhí)行派發(fā)更新的副作用函數(shù)。
// 存放所有需要派發(fā)更新的副作用函數(shù) let deps: (Dep | undefined)[] = []
接下來就根據(jù)操作類型type 和 key 來收集需要執(zhí)行派發(fā)更新的副作用函數(shù)。
如果操作類型是 TriggerOpTypes.CLEAR ,那么表示需要清除所有依賴,將當(dāng)前target的所有副作用函數(shù)添加到 deps 集合中。
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
// 當(dāng)需要清除依賴時,將當(dāng)前 target 的依賴全部傳入
deps = [...depsMap.values()]
}如果操作目標(biāo)是數(shù)組,并且修改了數(shù)組的 length 屬性,需要把與 length 屬性相關(guān)聯(lián)的副作用函數(shù)以及索引值大于或等于新的 length 值元素的相關(guān)聯(lián)的副作用函數(shù)從 depsMap 中取出并添加到 deps 集合中。
else if (key === 'length' && isArray(target)) {
// 如果操作目標(biāo)是數(shù)組,并且修改了數(shù)組的 length 屬性
depsMap.forEach((dep, key) => {
// 對于索引大于或等于新的 length 值的元素,
// 需要把所有相關(guān)聯(lián)的副作用函數(shù)取出并添加到 deps 中執(zhí)行
if (key === 'length' || key >= (newValue as number)) {
deps.push(dep)
}
})
} 如果當(dāng)前的 key 不為 undefined,則將與當(dāng)前key相關(guān)聯(lián)的副作用函數(shù)添加到 deps 集合中。注意這里的判斷條件 void 0,是通過 void 運算符的形式表示 undefined 。
if (key !== void 0) {
deps.push(depsMap.get(key))
}接下來通過 Switch 語句來收集操作類型為 ADD、DELETE、SET 時與 ITERATE_KEY 和 MAP_KEY_ITERATE_KEY 相關(guān)聯(lián)的副作用函數(shù)。
// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
// 操作類型為 ADD 時觸發(fā)Map 數(shù)據(jù)結(jié)構(gòu)的 keys 方法的副作用函數(shù)重新執(zhí)行
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// new index added to array -> length changes
deps.push(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
// 操作類型為 DELETE 時觸發(fā)Map 數(shù)據(jù)結(jié)構(gòu)的 keys 方法的副作用函數(shù)重新執(zhí)行
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
deps.push(depsMap.get(ITERATE_KEY))
}
break
}最后調(diào)用 triggerEffects 函數(shù),傳入收集的副作用函數(shù),執(zhí)行派發(fā)更新。
const eventInfo = __DEV__
? { target, type, key, newValue, oldValue, oldTarget }
: undefined
if (deps.length === 1) {
if (deps[0]) {
if (__DEV__) {
triggerEffects(deps[0], eventInfo)
} else {
triggerEffects(deps[0])
}
}
} else {
const effects: ReactiveEffect[] = []
// 將需要執(zhí)行的副作用函數(shù)收集到 effects 數(shù)組中
for (const dep of deps) {
if (dep) {
effects.push(...dep)
}
}
if (__DEV__) {
triggerEffects(createDep(effects), eventInfo)
} else {
triggerEffects(createDep(effects))
}
}triggerEffects 函數(shù)
export function triggerEffects(
dep: Dep | ReactiveEffect[],
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
// spread into array for stabilization
// 遍歷需要執(zhí)行的副作用函數(shù)集合
for (const effect of isArray(dep) ? dep : [...dep]) {
// 如果 trigger 觸發(fā)執(zhí)行的副作用函數(shù)與當(dāng)前正在執(zhí)行的副作用函數(shù)相同,則不觸發(fā)執(zhí)行
if (effect !== activeEffect || effect.allowRecurse) {
if (__DEV__ && effect.onTrigger) {
effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
}
if (effect.scheduler) {
// 如果一個副作用函數(shù)存在調(diào)度器,則調(diào)用該調(diào)度器
effect.scheduler()
} else {
// 否則直接執(zhí)行副作用函數(shù)
effect.run()
}
}
}
}在 triggerEffects 函數(shù)中,遍歷需要執(zhí)行的副作用函數(shù)集合,如果當(dāng)前副作用函數(shù)存在調(diào)度器,則執(zhí)行該調(diào)度器,否則直接執(zhí)行該副作用函數(shù)的 run 方法,執(zhí)行更新。
總結(jié)
本文深入分析了副作用的實現(xiàn)以及執(zhí)行時機(jī),并詳細(xì)分析了用于存儲副作用函數(shù)的targetMap的數(shù)據(jù)結(jié)構(gòu)及其實現(xiàn)原理。還深入分析了依賴收集track函數(shù)以及派發(fā)更新 trigger 函數(shù)的實現(xiàn)。Vue 在追蹤變化時,通過 track 函數(shù)收集依賴,即將副作用函數(shù)添加到 targetMap 中,通過 trigger 函數(shù)來執(zhí)行對應(yīng)的副作用函來完成更新。
到此這篇關(guān)于Vue3 源碼解讀之副作用函數(shù)與依賴收集的文章就介紹到這了,更多相關(guān)Vue3副作用函數(shù)與依賴收集內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
nuxt 每個頁面head標(biāo)簽內(nèi)容設(shè)置方式
這篇文章主要介紹了nuxt 每個頁面head標(biāo)簽內(nèi)容設(shè)置方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11
關(guān)于Vue父子組件傳參和回調(diào)函數(shù)的使用
這篇文章主要介紹了關(guān)于Vue父子組件傳參和回調(diào)函數(shù)的使用,我們將某段代碼封裝成一個組件,而這個組件又在另一個組件中引入,而引入該封裝的組件的文件叫做父組件,被引入的組件叫做子組件,需要的朋友可以參考下2023-05-05
VUE開發(fā)分布式醫(yī)療掛號系統(tǒng)后臺管理頁面步驟
本文從整體上介紹Vue框架的開發(fā)流程,結(jié)合具體的案例,使用Vue框架調(diào)用具體的后端接口,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
vue改變數(shù)據(jù)后數(shù)據(jù)變化頁面不刷新的解決方法
這篇文章主要給大家介紹了關(guān)于vue改變數(shù)據(jù)后數(shù)據(jù)變化頁面不刷新的解決方法,vue比較常見的坑就是數(shù)據(jù)(后臺返回)更新了,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
公共Hooks封裝文件下載useDownloadFile實例詳解
這篇文章主要為大家介紹了公共Hooks封裝文件下載useDownloadFile實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

