vue?3?effect作用與原理解析
Vue 3 的 Effect(副作用) 是整個響應(yīng)式系統(tǒng)的核心機制,負(fù)責(zé)管理依賴追蹤和響應(yīng)式觸發(fā)。理解其作用和原理對掌握 Vue 的底層機制至關(guān)重要。
一、核心作用
1. 依賴追蹤(Dependency Tracking)
- 自動跟蹤響應(yīng)式數(shù)據(jù)在副作用函數(shù)中的使用。
示例代碼:
import { reactive, effect } from 'vue'
const obj = reactive({ count: 0 })
effect(() => {
console.log(`count is: ${obj.count}`)
})- 當(dāng)首次執(zhí)行
effect時,函數(shù)() => console.log(...)會被運行。 - 觸發(fā)
obj.count的get操作,觸發(fā)依賴收集(將當(dāng)前effect關(guān)聯(lián)到obj.count)。
2. 自動響應(yīng)(Automatic Re-run)
當(dāng)響應(yīng)式數(shù)據(jù)的依賴變化時,自動重新執(zhí)行副作用函數(shù):
obj.count++ // 觸發(fā)依賴更新,控制臺打印 "count is: 1"
3. 支撐高級 API
computed、watch、組件渲染函數(shù)等底層都依賴于effect實現(xiàn)。
二、實現(xiàn)原理
1. 核心類:ReactiveEffect
Vue 3 用 ReactiveEffect 類封裝副作用邏輯,簡化后的源碼結(jié)構(gòu)如下:
class ReactiveEffect<T = any> {
// 當(dāng)前 effect 的所有依賴項(其他響應(yīng)式對象)
deps: Dep[] = []
// 構(gòu)造函數(shù)參數(shù)
constructor(
public fn: () => T, // 副作用函數(shù)
public scheduler?: () => void // 調(diào)度函數(shù)(控制重新執(zhí)行方式)
) {}
// 運行副作用(觸發(fā)依賴收集)
run() {
activeEffect = this // 標(biāo)記當(dāng)前正在運行的 effect
try {
return this.fn()
} finally {
activeEffect = undefined
}
}
// 停止偵聽
stop() { /* 從所有依賴中移除自身 */ }
}2. 依賴收集流程(Track)
數(shù)據(jù)結(jié)構(gòu):
type Dep = Set<ReactiveEffect> // 依賴集合 type TargetMap = WeakMap<Object, Map<string, Dep>> // 全局依賴存儲
- 觸發(fā)時機:響應(yīng)式數(shù)據(jù)的
get操作觸發(fā)時。 - 流程:
根據(jù)響應(yīng)式對象 (
target) 和鍵 (key) 找到存入targetMap的依賴集合 (dep)。將當(dāng)前活躍的
activeEffect添加到dep中。同時將
dep加入activeEffect.deps(反向記錄,用于 cleanup)。
3. 觸發(fā)更新(Trigger)
- 觸發(fā)時機:響應(yīng)式數(shù)據(jù)的
set操作時。 - 流程:
根據(jù)
target和key從targetMap獲取對應(yīng)的dep集合。遍歷
dep中所有effect:- 如果有
scheduler(如computed),執(zhí)行調(diào)度器(優(yōu)化性能)。 - 否則直接執(zhí)行
effect.run()。
- 如果有
4. 調(diào)度器(Scheduler)
允許控制 effect 如何重新執(zhí)行:
effect(() => {
console.log(obj.count)
}, {
scheduler(effect) {
// 如將 effect 推入微任務(wù)隊列中異步執(zhí)行
queueMicrotask(effect.run)
}
})- 應(yīng)用場景:
watch的異步批處理更新。computed的值懶更新。
三、關(guān)鍵優(yōu)化設(shè)計
1. 嵌套 Effect 棧
用棧結(jié)構(gòu) effectStack 跟蹤嵌套的 effect:
function run() {
if (!effectStack.includes(this)) {
try {
effectStack.push((activeEffect = this))
return this.fn()
} finally {
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
}
}
}- 解決問題:組件嵌套時的依賴關(guān)系混亂。
2. Cleanup 機制
每次 effect 執(zhí)行前清理舊依賴:
function run() {
cleanup(this) // 清理之前收集的舊依賴
// ...然后重新收集新依賴
}- 解決問題:動態(tài)分支邏輯導(dǎo)致的無效依賴(如
v-if切換導(dǎo)致的條件依賴)。
3. Lazy 執(zhí)行
可配置不立即執(zhí)行 effect:
const runner = effect(fn, { lazy: true })
runner() // 手動執(zhí)行- 應(yīng)用場景:
computed屬性初始化時延遲計算。
四、與 Vue 各組件的關(guān)聯(lián)
1. 組件渲染
組件 render 函數(shù)被包裹在 effect 中:
function setupRenderEffect(instance) {
effect(() => {
const subTree = instance.render.call(instance.proxy)
patch(instance.subTree, subTree)
instance.subTree = subTree
}, { scheduler: queueJob }) // 異步更新隊列
}2. Computed 實現(xiàn)
computed 通過 effect + 調(diào)度器實現(xiàn)懶更新:
const computedRef = new ComputedRefImpl(
getter,
() => { // 調(diào)度器
if (!this._dirty) {
this._dirty = true
trigger(this, 'set', 'value')
}
}
)3. Watch API
watch 基于 effect 的調(diào)度器實現(xiàn)異步回調(diào):
function watch(source, cb, { flush } = {}) {
let scheduler
if (flush === 'sync') {
scheduler = cb
} else { // 'post' 或其他默認(rèn)情況
scheduler = () => queuePostFlushCb(cb)
}
effect(() => traverse(source), { scheduler })
}五、與 Vue 2 的對比
| 特性 | Vue 2 (Watcher) | Vue 3 (Effect) |
|---|---|---|
| 依賴追蹤 | 通過遍歷數(shù)據(jù)觸發(fā) getter | 通過 Proxy/Reflect 自動追蹤 |
| 更新粒度 | 依賴組件級檢查 | 基于精確依賴的靶向更新 |
| 性能優(yōu)化 | 需手寫 pureComputed 等 | 內(nèi)置自動的依賴清理和調(diào)度機制 |
| 內(nèi)存管理 | 易產(chǎn)生內(nèi)存泄漏(舊 Dep 引用問題) | 通過 WeakMap 自動釋放無用依賴 |
六、源碼流程圖解
+---------------------+
| Reactive Object |
+----------+----------+
│ 訪問屬性時
▼
+---------------------+
| 觸發(fā) get 代理 +----→ track(target, key)
+---------------------+ │
▲ ▼ 存儲依賴關(guān)系
│ +---------------------+
+----------+ targetMap |
| (WeakMap結(jié)構(gòu)) |
+---------+-----------+
│
▼
+---------------------+
| depsMap (Map) |
| (key → Dep Set) |
+---------+-----------+
│
▼
+---------------------+
| dep (Set) |
| (存儲所有關(guān)聯(lián)的 effect)|
+---------------------+總結(jié)
Vue 3 的 effect 通過以下機制成為響應(yīng)式系統(tǒng)的核心:
- Proxy 依賴收集:精確追蹤響應(yīng)式數(shù)據(jù)的使用。
- 調(diào)度器控制:提供靈活的回調(diào)執(zhí)行方式。
- 內(nèi)存安全:通過
WeakMap自動管理依賴。 - 框架級優(yōu)化:支持組件渲染、計算屬性、watch 等核心功能。
到此這篇關(guān)于vue 3 effect作用與原理的文章就介紹到這了,更多相關(guān)vue 3 effect作用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中后端做Excel導(dǎo)出功能返回數(shù)據(jù)流前端的處理操作
這篇文章主要介紹了vue中后端做Excel導(dǎo)出功能返回數(shù)據(jù)流前端的處理操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
element-ui 表格實現(xiàn)單元格可編輯的示例
下面小編就為大家分享一篇element-ui 表格實現(xiàn)單元格可編輯的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02

