vue面試題
💡 一、響應式系統相關
?1. Vue 3 的響應式系統是如何實現的?和 Vue 2 有何本質區別?
答案:
Vue 3 使用 Proxy 實現響應式(位于 @vue/reactivity 模塊),替代 Vue 2 的 Object.defineProperty。
核心機制如下:
使用 targetMap: WeakMap 存儲依賴關系
利用 track() 和 trigger() 方法實現依賴收集與派發更新
effect() 包裝副作用函數,自動收集依賴
響應式對象通過 reactive() 創建,底層用 Proxy 攔截 get/set
本質區別:
Vue 2:只能監聽已有屬性,新增/刪除屬性需 Vue.set
Vue 3:完全代理對象,支持深層嵌套、數組、Map/Set
?2. Vue 3 是如何避免“無限遞歸依賴收集”的?
答案:
Vue 3 使用了 activeEffect 和 effectStack 來管理當前正在執行的副作用函數。
避免無限依賴循環的關鍵是:
if (shouldTrack && activeEffect) {
// 收集依賴
}
同時,在 effect() 中入棧前判斷是否已經存在于棧內。
💡 二、虛擬 DOM 與 Diff 算法
?3. Vue 3 的 patch 函數做了哪些優化?VNode 是如何分類處理的?
答案:
Vue 3 的 patch() 是遞歸處理新舊 vnode 的核心函數,優化包括:
利用 shapeFlag 標識節點類型,如:元素、組件、文本、Fragment 等
針對靜態節點進行 Static Hoisting,跳過 patch
Fragment 和 Text 類型的專門處理
children diff 使用 keyed diff 和 最長遞增子序列 算法,提升最小移動次數
VNode 創建時通過 createVNode() 自動計算 shapeFlag,以便 patch 階段快速判斷類型分支,提高性能。
💡 三、組件系統與生命周期
?4. setup() 中返回的內容是如何綁定到組件實例上的?
答案:
setup() 執行完后返回的對象會被綁定到組件代理實例上,使用 withProxy 包裝,在 render 中通過作用域代理訪問。
代碼核心位置:
const setupResult = setup(…)
// 將 setup 返回值代理到 ctx 上
instance.setupState = proxyRefs(setupResult)
最終通過 componentPublicInstance 的 proxy 實現 this.xxx 訪問 setup() 中的變量。
?5. defineComponent 的作用是什么?為什么 Vue 3 要使用它?
答案:
defineComponent 是一個類型輔助函數,本質上不影響運行時。其主要作用是配合 TS 推導組件類型,提升開發體驗。
Vue 3 中組件是基于對象或函數定義,defineComponent 會對組件定義做 TS 友好包裝,生成明確的類型聲明,使 IDE 和 TS 能正確推導 props、emits、slots 等。
💡 四、架構設計
?6. Vue 3 為何將核心功能模塊拆分為多個包?(如 reactivity、runtime-core)
答案:
Vue 3 的架構采用“模塊化 + 運行時編譯解耦”設計,目的包括:
按需打包:Tree shaking 支持更好
包職責清晰:如 @vue/reactivity 只管響應式邏輯
支持定制渲染器:如 Vue Native / Vue for MiniApp 可以復用 core,只替換 runtime
主要模塊:
reactivity:響應式系統
runtime-core:運行時核心邏輯
runtime-dom:瀏覽器環境渲染器
compiler-core:模板編譯核心
compiler-dom:專門為瀏覽器優化編譯器
💡 五、Composition API 與依賴收集
?7. ref() 和 reactive() 有什么區別?底層實現上有何不同?
答案:
ref() 創建一個包含 .value 的響應式對象,適合基本類型
reactive() 返回 Proxy 對象,適合對象類型
實現上:
ref() 底層是 RefImpl 類,使用 Object.defineProperty 對 .value 實現響應式
reactive() 使用 createReactiveObject() 返回 Proxy,內部使用統一的 mutableHandlers
function ref(value) {
return new RefImpl(value)
}
?8. computed 的懶執行機制是怎么實現的?
答案:
computed() 內部使用了 effect 并設置 lazy: true,不會立即執行計算,而是等 .value 被訪問時執行。
通過 dirty 標志判斷是否需要重新計算:
if (this._dirty) {
this._value = this.effect()
this._dirty = false
}
當依賴變化時,通過 scheduler 將 dirty 標志重新置為 true,延遲到下一次 .value 訪問時再計算。
💡 六、模板編譯與優化
?9. Vue 模板是如何轉換為渲染函數的?
答案:
模板編譯包含三步:
解析階段(Parse):將模板字符串轉為 AST(抽象語法樹)
轉換階段(Transform):AST 上做優化,如 hoist 靜態節點、v-if/v-for 編譯
生成階段(Codegen):將 AST 轉換為 JS 渲染函數代碼
最終由 compiler-core 輸出字符串形式的 render 函數,交由 Function 構造執行。
💡 七、深入 Diff 算法
?10. Vue 3 是如何優化 v-for 列表的 diff 性能的?關鍵點在哪?
答案:
Vue 3 中的 patchKeyedChildren 實現了高性能 diff,關鍵在:
四端對比法:從頭、尾同時比較新舊節點
key 查找優化:建立新節點的 key 映射表,加快查找
最長遞增子序列(LIS)算法:用于判斷哪些節點可以復用,避免不必要的移動操作
核心目標是通過 O(n) 時間復雜度實現最小 DOM 操作集合。
如果你有特定方向(如 Composition API 深挖、調度系統 Scheduler、Suspense 等),我可以繼續給你出更深入的源碼級問題。需要的話隨時告訴我!
🧩 Vue 3 API 實現原理總覽
Vue 3 源碼主要分布在以下幾個核心包中:
包名 說明
@vue/reactivity 響應式系統 API(如 ref, reactive)
@vue/runtime-core 組件系統、生命周期、渲染邏輯
@vue/runtime-dom DOM 渲染器(瀏覽器特有)
@vue/compiler-core 模板編譯核心
@vue/shared 公共工具函數
一、響應式 API(來自 @vue/reactivity)
? 1. reactive() 實現原理
ts
復制
編輯
function reactive(target) {
return createReactiveObject(target, isReadonly = false, baseHandlers)
}
使用 Proxy 攔截對象
內部通過 get, set, deleteProperty 實現依賴收集和觸發
使用 targetMap: WeakMap 存儲依賴映射(target -> key -> effects)
? 2. ref() 實現原理
ts
復制
編輯
class RefImpl {
get value() { track(this, ‘value’); return this._value }
set value(newVal) { this._value = newVal; trigger(this, ‘value’) }
}
適合基本類型
用 .value 包裝值并提供響應式訪問
內部也使用 track / trigger
? 3. computed() 實現原理
本質上是帶 lazy 標記的 effect
緩存值,依賴變化時重新計算
effect(() => getter(), {
lazy: true,
scheduler: () => markDirty()
})
? 4. watch() 與 watchEffect()
watchEffect:立即執行副作用,自動追蹤依賴
watch:可配置 source、deep、immediate 等選項
底層使用:
effect(fn, { scheduler: job => queueJob(job) })
二、組件 API 與生命周期(runtime-core)
? 5. defineComponent()
用于類型提示,在運行時是一個透傳函數
將組件包裝為標準格式 { setup, render, … }
? 6. setup() 機制
setup() 在組件初始化時執行
返回對象會通過 proxyRefs() 自動解包為代理
返回函數則作為 render 使用
? 7. 生命周期鉤子如 onMounted, onUpdated 實現原理
生命周期鉤子本質是注冊到當前組件實例的生命周期隊列中:
export function onMounted(fn) {
injectHook(‘mounted’, fn, currentInstance)
}
這些鉤子在組件 setup() 時通過 currentInstance 保存,在后續生命周期階段統一調用。
三、渲染函數相關 API
? 8. h() 函數
創建一個標準化的 VNode
function h(type, props, children) {
return createVNode(type, props, children)
}
type 可以是組件、HTML 標簽、Fragment、Comment 等
? 9. render() 函數
渲染 VNode 到容器,入口:
render(vnode, container)
內部調用 patch() 遞歸渲染,依類型分支執行不同邏輯(元素、組件、文本)
? 10. createApp() 實現原理
function createApp(rootComponent) {
return {
mount(container) {
const vnode = createVNode(rootComponent)
render(vnode, container)
}
}
}
包裝根組件 vnode
調用 render 渲染到容器上
支持多實例 app,隔離上下文
四、模板編譯相關 API(compiler-core)
Vue 模板編譯分三階段:
階段 描述
Parse 將模板字符串轉為 AST
Transform 優化 AST(靜態提升、合并表達式)
Codegen 輸出 JS 渲染函數
如:
html
復制
編輯
{{ msg }}
最終生成類似:function render(_ctx, _cache) {
return createVNode(“p”, null, toDisplayString(_ctx.msg))
}
五、內置指令與模塊(runtime-dom)
? 11. 指令 v-model 實現原理
在編譯階段轉為 modelValue prop + onUpdate:modelValue 事件
運行時由組件或平臺指令處理輸入/輸出雙向綁定邏輯
el.value = modelValue
el.addEventListener(‘input’, e => emit(‘update:modelValue’, e.target.value))
? 12. v-bind, v-on, v-if, v-for 實現
全部在編譯階段處理成 render 函數
編譯器生成不同的 patchFlag,用于運行時優化
例如 v-if:
isShow ? h(‘div’, …) : null
六、其它高級 API
? 13. provide() / inject()
使用組件實例內部的 provides 對象實現“原型鏈繼承”
instance.provides = Object.create(parent.provides)
provide(key, value) 存入當前實例
inject(key) 通過原型鏈查找父組件的 provide
? 14. Teleport、Suspense、KeepAlive 等內置組件
都是特殊類型的組件,在 patch 階段使用不同的處理邏輯
如 Teleport:
processTeleport(n1, n2, container, …)
🧠 總結
類型 API 原理關鍵點
響應式 reactive, ref, computed, watch Proxy 實現,track/trigger 管理依賴
組件系統 setup, defineComponent, 生命周期 組件實例管理生命周期與響應式綁定
渲染機制 h, render, patch VNode 虛擬樹遞歸對比與渲染
編譯相關 模板編譯生成 render 函數 模板 AST -> 渲染函數
其他 provide/inject, Teleport 實例上下文機制與特殊處理流程
如果你希望我針對某一類 API(如響應式、生命周期或編譯階段)深入挖掘源碼路徑、源碼調用鏈、調度機制,我可以繼續擴展。需要我分模塊講得更細嗎?
🔧 模塊 1:響應式系統(@vue/reactivity)
1.1 核心結構概覽
核心模塊 文件
響應式入口 reactive.ts, ref.ts
依賴收集 effect.ts
追蹤觸發 track.ts, trigger.ts
工具函數 baseHandlers.ts, reactiveEffect.ts
1.2 reactive() 實現流程
export function reactive(target) {
return createReactiveObject(target, false, mutableHandlers)
}
?? createReactiveObject:
創建 Proxy
使用 mutableHandlers 攔截 get/set
設置在 reactiveMap 中做緩存
?? 響應式觸發流程:
讀取值 → get() → track(target, key)
修改值 → set() → trigger(target, key)
1.3 effect() 與依賴收集
effect(fn, options)
會包裝為 ReactiveEffect 實例,保存到當前 activeEffect
在執行 fn 時,遇到響應式數據的 get() 會收集依賴
在 trigger 階段,從 targetMap 中找到 effect 列表執行
1.4 ref() / computed() 實現精講
class RefImpl {
get value() { track(this, ‘value’); return this._value }
set value(v) { this._value = v; trigger(this, ‘value’) }
}
ref 是響應式包裹器,適用于基本類型
computed 是懶加載、帶緩存的 effect
🧱 模塊 2:組件系統與運行時(@vue/runtime-core)
2.1 組件創建與掛載流程
關鍵函數: createVNode() → render() → patch() → mountComponent()
🔸 創建組件 VNode:
createVNode(Component, props)
🔸 掛載組件:
function mountComponent(initialVNode, container, parentComponent) {
const instance = createComponentInstance(initialVNode, parentComponent)
setupComponent(instance)
setupRenderEffect(instance, container)
}
2.2 setup() 執行流程
執行前設置 currentInstance
允許返回:
對象 → 綁定到 ctx
函數 → 用作 render
返回的響應式對象通過 proxyRefs() 自動展開 .value
2.3 生命周期鉤子注冊與執行
export function onMounted(fn) {
injectHook(‘mounted’, fn, currentInstance)
}
生命周期鉤子存儲在 instance.lifecycle 中
組件生命周期階段由 setupRenderEffect() 控制調度調用
2.4 defineComponent() 與類型系統
并不影響運行時
是為 TS 推導服務的輔助函數
export function defineComponent(options) {
return options
}
🎨 模塊 3:模板編譯(@vue/compiler-core)
3.1 編譯階段流程
compile(template, options)
=> parse() → transform() → generate()
🔸 parse.ts:解析 HTML 為 AST
html
復制
編輯
{ type: ‘Interpolation’, content: ‘msg’ }
🔸 transform.ts:AST 轉換優化
靜態提升(hoist)
插值優化
合并表達式
🔸 codegen.ts:生成代碼字符串
return function render(ctx) {
return createVNode(‘div’, null, toDisplayString(ctx.msg))
}
🖼? 模塊 4:渲染與虛擬 DOM(runtime-core + runtime-dom)
4.1 渲染流程
render(vnode, container)
內部調用 patch() 比較 vnode
根據 vnode 類型調用對應處理器(元素、組件、Fragment、Text)
4.2 Diff 算法優化點
使用 4 端比較法 + keyMap + 最長遞增子序列
提高節點重用率,降低移動成本
🚚 模塊 5:平臺特有模塊(@vue/runtime-dom)
5.1 DOM 渲染器封裝
createRenderer(options)
瀏覽器平臺實現 patchProp, insert, remove, setElementText
實現平臺無關性(可適配 Native、WebGL 等)
5.2 指令處理(v-model / v-show 等)
const directive = {
beforeMount(el, binding) { … },
updated(el, binding) { … }
}
編譯階段識別 v-model → 運行時使用 patchDOMProp 設置事件監聽
🧪 模塊 6:高級 API(teleport / suspense / keep-alive)
6.1 Teleport:跨 DOM 樹渲染
VNode 類型為 TELEPORT
patch 階段調用 processTeleport() 將子節點渲染到目標容器
6.2 KeepAlive:緩存組件實例
使用 LRU 緩存
復用舊實例,跳過創建 & 掛載階段
6.3 Suspense:異步組件占位符處理
提供 pending, resolve, fallback 邏輯
異步組件 resolve 前不渲染主內容
🧭 模塊 7:公共工具與輔助函數(@vue/shared)
isObject, isArray, hasOwn, makeMap
ShapeFlags, PatchFlags 常量
編譯 & 運行時共享函數
? 總結:各模塊職責對照表
模塊 文件包 關鍵職責
響應式 @vue/reactivity ref、reactive、effect、computed
組件系統 @vue/runtime-core 組件實例、生命周期、setup/render
渲染平臺 @vue/runtime-dom DOM 相關渲染器、平臺 API
編譯器 @vue/compiler-core 模板字符串 -> 渲染函數
特殊組件 @vue/runtime-core keep-alive、suspense、teleport
工具函數 @vue/shared 工具函數和枚舉常量
是否希望我進一步提供每個模塊的 源碼函數跳轉路徑(比如從 reactive() 進入到 createReactiveObject(),再進入 mutableHandlers.get() 具體實現)?或者針對某一類進行源碼級逐行解析?可以按需細化 👇
您說:
vue所有api的使用場景,以及對應的場景面試題目,我需要很詳細
ChatGPT 說:
非常好,這里我將為你整理 Vue 3 所有核心 API 的使用場景、對應源碼機制簡析,并為每個 API 設計一個針對性面試題目(含答題要點),幫助你進行系統性理解與模擬答題訓練。
🧩 Vue 3 API 使用場景與面試題(詳細完整版)
以下按照模塊進行劃分,包括響應式、組件、生命周期、渲染、工具等。
🔧 一、響應式系統 API
API 使用場景 面試題 答題要點
reactive() 對復雜對象進行響應式包裝 ? Vue 3 中 reactive 是如何實現的?使用時有哪些注意點? 使用 Proxy;嵌套深層自動遞歸代理;不能代理原始類型;track/trigger 系統實現
ref() 包裝基本類型(string/number)使其響應式;或用于 DOM/組件引用 ? ref 與 reactive 的區別及其適用場景? ref 適用于基本類型;.value 存取;內部封裝 RefImpl
computed() 派生值緩存;依賴變化時重新計算 ? computed 是如何實現懶計算與緩存機制的? 使用 lazy effect;首次訪問觸發執行;內部使用 dirty 標志
watch() 監聽響應式數據的變化并執行副作用邏輯 ? watch 和 watchEffect 有什么區別? watch 明確指定源;默認懶執行;可指定 flush;watchEffect 自動收集依賴
watchEffect() 自動依賴收集的響應式副作用,適合頁面通用邏輯監聽 ? watchEffect 什么時候執行?會監聽哪些依賴? 立即執行;內部自動調用 effect;依賴變化觸發重新運行
shallowReactive() 創建淺層代理,避免深度遞歸提升性能 ? shallowReactive 的作用及使用時機? 對象頂層響應式;深層屬性不是響應式
customRef() 自定義 getter/setter 邏輯,控制依賴追蹤/觸發 ? 如何用 customRef 實現一個節流響應? 返回帶 track/trigger 的自定義包裝器;手動控制響應更新時機
readonly() 創建只讀的響應式對象 ? readonly 和 reactive 的區別是?是否可以被修改? 創建只讀 Proxy;set 時控制臺報警;內部使用 readonlyHandlers
toRefs() 將響應式對象的屬性變成 ref,常用于 setup() 返回值解構 ? 為什么在 setup 返回對象時推薦使用 toRefs()? 保留響應性;避免解構丟失響應
🧱 二、組件與 Composition API
API 使用場景 面試題 答題要點
defineComponent() 組件定義時獲得更好的類型推導支持 ? defineComponent 有什么作用?是運行時函數嗎? 類型輔助工具函數;不影響運行時邏輯
setup() 函數組件入口;響應式邏輯、生命周期注冊都在其中完成 ? setup 的執行時機與作用?返回什么? 在 beforeCreate 之前;返回對象 → context,返回函數 → render
getCurrentInstance() 獲取當前組件實例(開發工具、注冊狀態等) ? getCurrentInstance 應該在什么地方使用? 只能在 setup 或 生命周期鉤子 中使用
provide / inject 實現跨層級依賴注入(如:主題、全局配置) ? provide/inject 是如何實現組件通信的? 通過組件實例 provides 鏈;inject 會查找父組件上下文
defineAsyncComponent() 定義異步組件(支持 loading/error 組件) ? 異步組件在 Vue 3 中是如何加載與錯誤處理的? 支持 loader、loadingComponent、timeout、errorComponent 等配置項
isRef / isReactive 判斷某變量是否是 ref 或響應式對象 ? 判斷響應式對象類型有哪些方法? isRef()、isReactive()、isProxy()
?? 三、生命周期 API
API 使用場景 面試題 答題要點
onMounted() DOM 渲染后執行邏輯,如綁定事件或請求 ? 生命周期鉤子在 setup 中是如何注冊并觸發的? 注入到實例對應的鉤子數組中,在組件掛載/更新階段統一調用
onUpdated() 數據更新后執行邏輯 ? 在哪個階段訪問的 DOM 一定是最新的? onUpdated;組件完成更新之后
onUnmounted() 清理資源(如:定時器、事件) ? 如何防止組件卸載時內存泄漏? onUnmounted 中清除副作用邏輯
onBeforeMount() 等 準確控制生命周期過程 ? onBeforeMount 和 onMounted 的執行順序? 掛載前后;setup 中注冊的鉤子按聲明順序注冊
🖼? 四、渲染與模板相關
API 使用場景 面試題 答題要點
h() 手動創建 vnode,如 JSX、動態組件場景 ? h() 函數的參數結構是什么?最終返回什么? (type, props?, children?);返回 VNode 對象
createApp() 創建獨立應用實例,支持多實例 ? createApp 支持哪些方法?和 Vue 2 有哪些不同? 提供掛載、插件注冊、全局組件等方法
render() 手動渲染 vnode(如 Teleport、自定義容器) ? render() 是如何將 VNode 渲染到容器上的? 通過 patch 遍歷 vnode 樹并掛載到 DOM
Teleport DOM 跨層級渲染(如:彈窗) ? Teleport 的實現機制?如何指定目標容器? 類型為 TELEPORT; render 到指定 DOM 容器
KeepAlive 緩存組件實例(如:tab 頁面切換) ? 如何緩存組件狀態?KeepAlive 的作用? 保留組件狀態不被卸載;支持 include/exclude
Suspense 渲染異步組件時提供 fallback ? Suspense 的工作原理?在哪些場景適用? 異步組件渲染前先展示 fallback;等待所有 async setup 完成后替換內容
🛠? 五、工具函數類
API 使用場景 面試題 答題要點
markRaw() 阻止對象被代理(如大型類庫對象) ? 如何避免某對象被 Vue 變成響應式? 使用 markRaw;或放入 shallowReactive 外層
toRaw() 獲取原始對象 ? 如何獲取 reactive 對象的原始值? toRaw(obj);但慎用,會丟失響應性
unref() 兼容普通值和 ref;常用于模板/工具函數中 ? unref 在模板中有用嗎? 有用;如 unref(props.count);自動解包 ref
proxyRefs() 把對象中 ref 自動展開為值訪問 ? proxyRefs 是如何優化模板解構的? 訪問自動加 .value;修改自動轉 ref.value
📦 六、編譯器(運行時配合)
場景 使用 API 面試題 答題要點
模板編譯 compile() ? Vue 模板編譯過程包括哪幾步? parse → transform → codegen;最終生成 render 函數
動態指令 v-bind、v-on ? v-bind 如何轉化為 prop 賦值? 編譯階段轉為 vnode props;運行時設置 DOM 屬性或組件 props
動態組件 component + is ? 動態組件渲染過程是怎樣的? 通過 渲染不同組件 VNode
v-model 編譯 modelValue + update:modelValue ? Vue 3 中 v-model 編譯后生成了什么? 雙向綁定 props 和 emit;支持多個 v-model 綁定名
🧠 Bonus:實戰類面試題(綜合考查)
? 1. 如何實現一個帶緩存功能的搜索組件?
👉 應用:ref + watch + debounce + cache Map
? 2. 在組件 setup() 中使用 ref 解構對象屬性后失去響應性,該如何處理?
👉 答案:使用 toRefs() 或 reactive + 結構展開
? 3. 如何在組件內部優雅實現節流的輸入監聽?
👉 使用 watchEffect + customRef
? 總結
我們從使用場景出發,為 Vue 3 的 50+ 核心 API 設計了面試題,每一題都圍繞實戰、源碼原理和使用注意事項展開。你可以按模塊系統性訓練。
如果你希望我將這些面試題匯總成文檔或導出為面試題清單 PDF、markdown 文件,或想深入某個模塊(如響應式依賴調度、patch diff、異步組件加載)我可以為你定制深入講解。是否需要我繼續細化某一類?
🔧 模塊一:響應式依賴收集(Dep 收集)
? 概念講解:
Vue 3 使用 Proxy 重寫了 Vue 2 中基于 Object.defineProperty 的響應式系統。
核心模塊:
@vue/reactivity
effect.ts:響應式副作用函數
reactive.ts:創建響應式對象
track.ts / trigger.ts:依賴收集與觸發更新
effect(() => {
console.log(state.count) // 自動收集依賴
})
依賴是如何收集的?
訪問響應式屬性時觸發 get → 執行 track() → 記錄當前活躍的 effect。
修改屬性時觸發 set → 執行 trigger() → 找到所有依賴這個屬性的副作用函數重新執行。
🎯 面試題 1:
Q:Vue 3 中是如何進行依賴收集的?effect 與 reactive 如何協作?
答:
Vue 3 使用了 Proxy 攔截 get 與 set 操作。當 effect 函數執行時,設置為 activeEffect。讀取響應式屬性時會通過 track() 將 target -> key -> Set 記錄在 targetMap 中。屬性變更后通過 trigger() 查找并重新執行依賴該屬性的 effect。
🔧 模塊二:響應式對象的實現 reactive/ref/toRef 等
? 概念講解:
reactive(obj):深層響應式對象
ref(val):單值響應式
toRef(obj, key):保持屬性響應式
readonly():只讀代理
🎯 面試題 2:
Q:ref 和 reactive 有什么區別?分別適用于什么場景?
答:
ref 適用于基本類型或需要 .value 解包的響應式處理。
reactive 適用于對象或數組,自動遞歸深度響應式。
實際開發中,ref 常用于組合式 API 處理局部狀態或基本類型狀態,reactive 適用于表單對象、組件狀態等結構性更復雜的數據。
🔧 模塊三:調度與更新機制(Scheduler & Job Queue)
? 概念講解:
在 effect 中可以指定一個 scheduler 函數,Vue 利用這個機制進行批處理。
ts
復制
編輯
effect(fn, {
scheduler: (job) => queueJob(job)
})
Vue 通過一個微任務隊列 queueJob 實現合并更新,避免多次無效 DOM 操作。
🎯 面試題 3:
Q:Vue 是如何避免重復執行 effect 的?微任務隊列在這里起了什么作用?
答:
Vue 會將帶 scheduler 的副作用函數加入到一個 Set 隊列中,使用 Promise.then() 作為微任務調度,在下一個微任務周期內統一執行。使用 Set 可以自動去重。
🔧 模塊四:嵌套依賴追蹤與優化
? 概念講解:
嵌套 effect 可能導致依賴記錄出錯,Vue 使用一個棧 effectStack 管理當前活躍的 effect。
function effect(fn) {
const e = () => {
cleanup(e)
activeEffect = e
effectStack.push(e)
fn()
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
}
}
🎯 面試題 4:
Q:Vue 如何處理嵌套 effect 場景?
答:
通過維護一個 effectStack 棧來避免 effect 被誤記錄。進入 effect 時入棧,執行完出棧,恢復上一個活躍的 effect。
🔧 模塊五:依賴清理與 stop 功能
? 概念講解:
為了防止內存泄露,Vue 在每次 effect 執行前會清理舊的依賴。
ts
復制
編輯
function cleanup(effect) {
effect.deps.forEach(dep => dep.delete(effect))
effect.deps.length = 0
}
也支持 stop(effect) 停止某個響應式副作用追蹤。
🎯 面試題 5:
Q:為什么 Vue 要清除舊的依賴?stop() 的原理是什么?
答:
effect 中依賴可能會變化(如分支判斷),為了避免冗余追蹤和錯誤更新,需要在重新執行前移除舊依賴。stop() 則是提前清除依賴,并防止 effect 被再次追蹤。
🧠 場景題:模擬面試官提問風格
Q6:我現在在組件中用了 reactive 對象和多個 ref,每個都綁定在表單控件上,修改任意控件都會觸發 render。你能講講這是如何實現的嗎?能否避免頻繁 render?
答:
每個響應式屬性都有對應的依賴,當這些屬性在組件 render 中被訪問,會收集依賴(track)。一旦屬性變更,trigger 會通知 effect 調度更新組件。可以通過:
watch 局部監聽
shallowReactive 控制深度
debounce 合理延遲更新
來降低頻繁 render 的問題。
? 總結:可擴展的面試提綱
模塊 涉及 API 推薦面試題 源碼文件
響應式核心 reactive, ref, track, trigger Vue 響應式原理及依賴收集機制 reactivity/reactive.ts, effect.ts
調度更新 effect scheduler Job Queue 如何調度更新? scheduler.ts
嵌套 effect 多層依賴追蹤 Vue 如何處理嵌套副作用函數? effect.ts
依賴清理 stop, cleanup 為什么要清理依賴? effect.ts
實踐場景 表單、組合式 API 如何管理復雜響應式狀態? 綜合應用