深入解析Vue包之vue各子包究竟導(dǎo)出了什么
前言
本文系統(tǒng)梳理 Vue 3 各子包(vue、@vue/reactivity、@vue/runtime-core、@vue/runtime-dom、@vue/compiler-core、@vue/compiler-dom、@vue/shared)的完整導(dǎo)出結(jié)構(gòu),重點(diǎn)覆蓋官方文檔未詳細(xì)說明、日常開發(fā)中被忽視但架構(gòu)價(jià)值極高的 API。
一、Vue 包體系全景:Monorepo 架構(gòu)
很多開發(fā)者誤認(rèn)為 vue 是一個(gè)完整的實(shí)現(xiàn)包,實(shí)則它只是一個(gè) Facade(門面模塊) :
vue(聚合導(dǎo)出層) │ ├── @vue/runtime-dom ← 瀏覽器平臺(tái)渲染層 │ └── @vue/runtime-core ← 平臺(tái)無關(guān)運(yùn)行時(shí) │ └── @vue/reactivity ← 純響應(yīng)式系統(tǒng) │ ├── @vue/compiler-dom ← 瀏覽器模板編譯器 │ └── @vue/compiler-core ← 平臺(tái)無關(guān)編譯器 │ └── @vue/shared ← 內(nèi)部共享工具函數(shù)
每個(gè)子包都可以獨(dú)立安裝使用,這正是 Vue 3 架構(gòu)解耦的核心體現(xiàn)。
二、@vue/reactivity:獨(dú)立響應(yīng)式引擎
這是 Vue 3 最具突破性的設(shè)計(jì)——響應(yīng)式系統(tǒng)與框架完全解耦,可以在任何 JS 環(huán)境中獨(dú)立使用。
npm install @vue/reactivity
2.1 核心響應(yīng)式原語
ref/shallowRef/customRef
import { ref, shallowRef, customRef } from '@vue/reactivity'
// 標(biāo)準(zhǔn) ref:深度響應(yīng)
const state = ref({ a: { b: 1 } })
// shallowRef:只有 .value 本身是響應(yīng)式,內(nèi)部屬性不追蹤
const shallow = shallowRef({ count: 0 })
shallow.value.count++ // 不觸發(fā)更新
shallow.value = { count: 1 } // 觸發(fā)更新
// customRef:完全自定義追蹤時(shí)機(jī)(防抖、節(jié)流場(chǎng)景極為有用)
function useDebouncedRef(value, delay = 200) {
let timer
return customRef((track, trigger) => ({
get() {
track() // 手動(dòng)聲明依賴追蹤
return value
},
set(newValue) {
clearTimeout(timer)
timer = setTimeout(() => {
value = newValue
trigger() // 手動(dòng)觸發(fā)更新
}, delay)
}
}))
}
customRef 是實(shí)現(xiàn)防抖輸入、異步數(shù)據(jù)源等場(chǎng)景的底層利器,但鮮少被人使用。
reactive/shallowReactive/readonly/shallowReadonly
import { reactive, shallowReactive, readonly, shallowReadonly } from '@vue/reactivity'
// shallowReactive:只追蹤頂層屬性,不深度轉(zhuǎn)換
const state = shallowReactive({
user: { name: 'Tom' } // user.name 的變化不會(huì)觸發(fā)更新
})
// readonly:深度只讀代理,任何寫入都會(huì)在 dev 環(huán)境報(bào)警告
const config = readonly({ api: 'https://example.com' })
// shallowReadonly:只有頂層只讀,嵌套對(duì)象仍可修改
const mixed = shallowReadonly({ nested: { value: 1 } })
mixed.nested.value = 2 // 允許(不報(bào)警告)
mixed.nested = {} // 阻止(報(bào)警告)
2.2 副作用系統(tǒng)底層 API(極少被直接使用)
effect/ReactiveEffect
effect 是 Vue 響應(yīng)式系統(tǒng)最底層的副作用原語,computed、watch 都是基于它構(gòu)建的。
import { effect, reactive } from '@vue/reactivity'
const state = reactive({ count: 0 })
// effect 會(huì)立即執(zhí)行,并自動(dòng)追蹤其中訪問的響應(yīng)式數(shù)據(jù)
const runner = effect(() => {
console.log('count is:', state.count)
})
state.count++ // 自動(dòng)重新執(zhí)行 effect
// 手動(dòng)停止追蹤
runner.effect.stop()
effectScope/getCurrentScope/onScopeDispose
這組 API 是 Vue 3.2 引入的副作用作用域管理機(jī)制,專為編寫可復(fù)用組合函數(shù)設(shè)計(jì),但日常業(yè)務(wù)代碼中極少被直接使用。
import { effectScope, getCurrentScope, onScopeDispose } from '@vue/reactivity'
// 創(chuàng)建一個(gè)作用域,批量管理所有 effect/watch/computed
const scope = effectScope()
scope.run(() => {
const doubled = computed(() => count.value * 2)
watchEffect(() => console.log(doubled.value))
})
// 一次性停止作用域內(nèi)所有副作用——無需逐一手動(dòng) stop()
scope.stop()
架構(gòu)價(jià)值:在大型組合函數(shù)庫(如 VueUse)中,effectScope 是正確管理副作用生命周期的標(biāo)準(zhǔn)方式,避免內(nèi)存泄漏。
// 在自定義組合函數(shù)中使用
function useFeature() {
const scope = effectScope()
scope.run(() => {
// 所有 effect 在這里注冊(cè)
onScopeDispose(() => {
// scope 被 stop 時(shí)的清理邏輯
scope.stop()
})
})
return scope
}
pauseTracking/resumeTracking/resetTracking
這組 API 允許手動(dòng)控制依賴追蹤的暫停與恢復(fù),是構(gòu)建高級(jí)響應(yīng)式工具的基礎(chǔ)。
import { pauseTracking, resetTracking, effect } from '@vue/reactivity'
effect(() => {
state.a // 正常追蹤
pauseTracking()
state.b // 訪問但不追蹤,state.b 變化不會(huì)觸發(fā) re-run
resetTracking()
state.c // 恢復(fù)正常追蹤
})
實(shí)際應(yīng)用場(chǎng)景:在實(shí)現(xiàn) readonly 包裝、不希望某些讀操作建立依賴時(shí)使用。
trackOpBit/triggerRef
import { ref, triggerRef } from '@vue/reactivity'
// triggerRef:強(qiáng)制觸發(fā) shallowRef 的更新(直接修改內(nèi)部結(jié)構(gòu)后手動(dòng)通知)
const state = shallowRef({ list: [1, 2, 3] })
state.value.list.push(4) // 直接改內(nèi)部,不觸發(fā)更新
triggerRef(state) // 手動(dòng)強(qiáng)制觸發(fā)
2.3 工具函數(shù)
toRaw/markRaw
import { reactive, toRaw, markRaw } from '@vue/reactivity'
const state = reactive({ data: {} })
// toRaw:獲取響應(yīng)式對(duì)象的原始對(duì)象(繞過 Proxy),用于性能敏感操作
const raw = toRaw(state) // 直接操作,不觸發(fā)任何追蹤
// markRaw:標(biāo)記對(duì)象永不被轉(zhuǎn)為響應(yīng)式(第三方庫實(shí)例、大型數(shù)據(jù)集等)
const chart = markRaw(new ECharts())
const state2 = reactive({ chart }) // chart 不會(huì)被代理
架構(gòu)意義:在將第三方庫對(duì)象(如 Canvas 實(shí)例、WebSocket 對(duì)象)放入響應(yīng)式狀態(tài)時(shí),必須用 markRaw 避免性能問題。
proxyRefs
import { proxyRefs, ref } from '@vue/reactivity'
// proxyRefs:自動(dòng)解包對(duì)象中的所有 ref(這正是 setup() 返回值的內(nèi)部處理機(jī)制)
const state = { count: ref(0), name: ref('Tom') }
const proxied = proxyRefs(state)
proxied.count // 直接訪問,無需 .value(內(nèi)部自動(dòng)解包)
這是 Vue 模板自動(dòng)解包 ref 的底層實(shí)現(xiàn)機(jī)制。
isRef/isReactive/isReadonly/isProxy/isShallow
import { ref, reactive, readonly, isRef, isReactive, isReadonly, isProxy, isShallow, shallowRef } from '@vue/reactivity'
isRef(ref(0)) // true
isReactive(reactive({})) // true
isReadonly(readonly({})) // true
isProxy(reactive({})) // true(reactive 和 readonly 都是 Proxy)
isShallow(shallowRef(0)) // true(Vue 3.2+ 新增)
unref
import { unref, ref } from '@vue/reactivity'
// unref:安全地獲取 ref 的值,非 ref 原樣返回
function useValue(val) {
return unref(val) // 不需要判斷 isRef
}
useValue(ref(42)) // 42
useValue(42) // 42
這是編寫接受 MaybeRef<T> 參數(shù)的組合函數(shù)的標(biāo)準(zhǔn)做法。
toRef/toRefs/toValue(Vue 3.3+)
import { reactive, toRef, toRefs, toValue } from '@vue/reactivity'
const state = reactive({ x: 1, y: 2 })
// toRef:從 reactive 對(duì)象中創(chuàng)建單個(gè)屬性的 ref(保持響應(yīng)式連接)
const xRef = toRef(state, 'x')
// toRefs:將整個(gè) reactive 對(duì)象解構(gòu)為 ref 集合(解構(gòu)時(shí)保持響應(yīng)性的核心工具)
const { x, y } = toRefs(state)
// toValue(3.3+):同 unref,但還能接受 getter 函數(shù)
const val = toValue(() => state.x + 1) // 執(zhí)行 getter 并返回結(jié)果
2.4 計(jì)算屬性底層
computed的完整簽名
import { computed } from '@vue/reactivity'
// 可寫計(jì)算屬性(文檔有提及但很少被用到)
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => { count.value = val - 1 }
})
plusOne.value = 10
console.log(count.value) // 9
三、@vue/runtime-core:平臺(tái)無關(guān)運(yùn)行時(shí)
這是 Vue 組件模型的核心,包含大量未被官方文檔重點(diǎn)介紹的底層 API。
3.1 組件內(nèi)部訪問
getCurrentInstance
import { getCurrentInstance } from '@vue/runtime-core'
// 只能在 setup() 或生命周期鉤子中調(diào)用
const instance = getCurrentInstance()
// 實(shí)例上有大量?jī)?nèi)部屬性(僅用于框架級(jí)開發(fā),生產(chǎn)代碼慎用)
instance.uid // 組件唯一 ID
instance.type // 組件定義對(duì)象
instance.parent // 父組件實(shí)例
instance.root // 根組件實(shí)例
instance.appContext // 應(yīng)用上下文(含全局注冊(cè)的組件、指令、插件數(shù)據(jù))
instance.subTree // 當(dāng)前 vnode 子樹
instance.isMounted // 是否已掛載
instance.isUnmounted // 是否已卸載
場(chǎng)景:編寫需要感知組件樹的底層庫(如 Pinia 內(nèi)部通過它訪問 appContext)。
useAttrs/useSlots
import { useAttrs, useSlots } from '@vue/runtime-core'
// 在 setup 中訪問透?jìng)鞯?attrs 和 slots(等效于選項(xiàng)式 API 的 $attrs/$slots)
const attrs = useAttrs() // 包含 class, style, 事件監(jiān)聽器等非 prop 屬性
const slots = useSlots() // 包含所有插槽的渲染函數(shù)
useCssModule(與<style module>配合)
import { useCssModule } from '@vue/runtime-core'
// 訪問 CSS Modules 的類名映射(通常與 SFC <style module> 配合)
const css = useCssModule()
// css.myClass → 'myClass_hash123_1'(編譯后的哈希類名)
3.2 依賴注入高級(jí)用法
inject的完整簽名與InjectionKey
import { provide, inject, InjectionKey } from '@vue/runtime-core'
// 使用 Symbol 作為類型安全的注入鍵(TypeScript 推薦做法)
const UserKey: InjectionKey<{ name: string }> = Symbol('user')
// 父組件
provide(UserKey, { name: 'Tom' })
// 子組件 - 有完整類型推斷
const user = inject(UserKey) // User | undefined
const user2 = inject(UserKey, { name: 'Guest' }) // 帶默認(rèn)值,類型為 User
const user3 = inject(UserKey, () => ({ name: 'Guest' }), true) // 工廠函數(shù)(第三個(gè)參數(shù) true 表示是工廠)
3.3 VNode 操作 API
h渲染函數(shù)的完整能力
import { h, resolveComponent, resolveDirective, withDirectives } from '@vue/runtime-core'
export default {
render() {
// 渲染組件
const MyComp = resolveComponent('MyComp') // 按名稱解析已注冊(cè)的全局組件
// 渲染帶自定義指令的 vnode
const dir = resolveDirective('my-directive')
return withDirectives(
h('div', 'hello'),
[[dir, value, argument, modifiers]]
)
}
}
cloneVNode
import { h, cloneVNode } from '@vue/runtime-core'
// 克隆 VNode 并可覆蓋部分 props(用于高階組件 HOC 模式)
const original = h('div', { class: 'foo' }, 'hello')
const cloned = cloneVNode(original, { class: 'bar', style: 'color: red' })
// 等效于 h('div', { class: ['foo', 'bar'], style: 'color: red' }, 'hello')
mergeProps
import { mergeProps } from '@vue/runtime-core'
// 智能合并多個(gè) props 對(duì)象(class/style 合并,事件監(jiān)聽器鏈?zhǔn)秸{(diào)用)
const merged = mergeProps(
{ class: 'a', onClick: handler1 },
{ class: 'b', onClick: handler2 }
)
// { class: ['a', 'b'], onClick: [handler1, handler2] }
這是實(shí)現(xiàn)透?jìng)鲗傩院喜ⅰ⒎庋b UI 組件庫的核心工具。
createVNode/openBlock/createBlock/createElementVNode
這些是編譯器輸出的運(yùn)行時(shí)助手函數(shù),通常不應(yīng)在業(yè)務(wù)代碼中直接使用,但理解它們有助于分析編譯產(chǎn)物性能。
// 模板編譯后生成的代碼示意:
import { createElementVNode, openBlock, createElementBlock } from '@vue/runtime-core'
function render() {
return (openBlock(), createElementBlock("div", null, [
createElementVNode("span", null, "hello")
]))
}
openBlock + createBlock 組合是 Vue 3 **Fragment 優(yōu)化與靶向更新(patch flag)**的基礎(chǔ)。
createTextVNode/createCommentVNode/createStaticVNode
import { createTextVNode, createCommentVNode, createStaticVNode } from '@vue/runtime-core'
createTextVNode('hello') // 創(chuàng)建文本節(jié)點(diǎn)
createCommentVNode('debug info') // 創(chuàng)建注釋節(jié)點(diǎn)(v-if 的占位符就是它)
// createStaticVNode:將靜態(tài) HTML 字符串作為 vnode(SSR hydration 場(chǎng)景)
createStaticVNode('<div>static content</div>', 1)
3.4 異步組件
defineAsyncComponent完整配置
import { defineAsyncComponent } from '@vue/runtime-core'
const AsyncComp = defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: LoadingSpinner, // 加載中顯示的組件
delay: 200, // 顯示 loading 前的延遲(避免閃爍)
errorComponent: ErrorDisplay, // 加載失敗顯示的組件
timeout: 3000, // 超時(shí)時(shí)間(ms),超時(shí)視為失敗
// 高級(jí):自定義加載函數(shù)(控制重試邏輯)
onError(error, retry, fail, attempts) {
if (attempts <= 3) retry() // 最多重試 3 次
else fail()
}
})
3.5 內(nèi)置組件的底層實(shí)現(xiàn)
Suspense深度用法
import { Suspense, defineAsyncComponent } from 'vue'
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<LoadingSpinner />
</template>
</Suspense>
Suspense 還支持與 async setup() 配合:
export default {
async setup() {
// setup 可以是異步的,父級(jí) Suspense 會(huì)等待它 resolve
const data = await fetch('/api/data').then(r => r.json())
return { data }
}
}
3.6 調(diào)度器 API
nextTick與內(nèi)部調(diào)度機(jī)制
import { nextTick } from '@vue/runtime-core'
// nextTick 返回 Promise,可在 DOM 更新后執(zhí)行操作
await nextTick()
// 也接受回調(diào)(兼容舊寫法)
nextTick(() => {
// DOM 已更新
})
queuePostFlushCb(內(nèi)部 API,謹(jǐn)慎使用)
import { queuePostFlushCb } from '@vue/runtime-core'
// 在當(dāng)前刷新周期結(jié)束后異步執(zhí)行回調(diào)(比 nextTick 更底層)
queuePostFlushCb(() => {
// 所有 DOM 更新和 watchPostEffect 之后執(zhí)行
})
3.7 生命周期擴(kuò)展鉤子
除了常見的八個(gè)生命周期,還有以下較少使用的:
import {
onRenderTracked, // 每次響應(yīng)式依賴被追蹤時(shí)觸發(fā)(僅 dev)
onRenderTriggered, // 每次重渲染被觸發(fā)時(shí)攜帶具體原因(僅 dev)
onActivated, // KeepAlive 激活時(shí)
onDeactivated, // KeepAlive 停用時(shí)
onServerPrefetch, // SSR:組件 prefetch 數(shù)據(jù)鉤子
onErrorCaptured, // 捕獲子孫組件的錯(cuò)誤
} from '@vue/runtime-core'
// onRenderTracked / onRenderTriggered 是性能調(diào)試?yán)?
onRenderTriggered((event) => {
console.log('觸發(fā)重渲染的原因:', {
effect: event.effect,
target: event.target, // 被修改的對(duì)象
type: event.type, // 'set' / 'add' / 'delete'
key: event.key, // 被修改的屬性名
newValue: event.newValue,
oldValue: event.oldValue,
})
})
onRenderTracked 和 onRenderTriggered 是排查不必要重渲染的終極工具,但極少被開發(fā)者使用。
3.8defineComponent的類型推斷能力
import { defineComponent, PropType } from '@vue/runtime-core'
// defineComponent 的核心價(jià)值是 TypeScript 類型推斷,而非運(yùn)行時(shí)行為
const MyComp = defineComponent({
props: {
user: {
type: Object as PropType<{ name: string; age: number }>,
required: true
},
// 函數(shù)類型 prop
onClick: Function as PropType<(id: number) => void>
},
emits: {
// 帶類型驗(yàn)證的 emits 定義
change: (value: string) => typeof value === 'string',
submit: null // 無驗(yàn)證
},
setup(props, { emit, expose, attrs, slots }) {
// props 此處有完整類型推斷
expose({ publicMethod() {} }) // 限制對(duì)外暴露的 API
}
})
3.9 渲染器相關(guān)(高級(jí))
createRenderer/createHydrationRenderer
這是 Vue 跨平臺(tái)渲染的核心 API,允許創(chuàng)建自定義渲染器。
import { createRenderer } from '@vue/runtime-core'
// 創(chuàng)建一個(gè) Canvas 渲染器(偽代碼示意)
const { render, createApp } = createRenderer({
createElement(type) { /* 創(chuàng)建 Canvas 元素 */ },
patchProp(el, key, prevVal, nextVal) { /* 更新屬性 */ },
insert(el, parent, anchor) { /* 插入節(jié)點(diǎn) */ },
remove(el) { /* 刪除節(jié)點(diǎn) */ },
createText(text) { /* 創(chuàng)建文本節(jié)點(diǎn) */ },
setText(node, text) { /* 更新文本 */ },
// ...其他 DOM 操作
})
這是 Pixi.js、Three.js、Native 渲染器等 Vue 跨平臺(tái)項(xiàng)目的基礎(chǔ) API。
四、@vue/runtime-dom:瀏覽器平臺(tái)層
在 runtime-core 之上,runtime-dom 提供了瀏覽器專屬能力。
4.1 內(nèi)置指令實(shí)現(xiàn)
這些通常由編譯器自動(dòng)生成,但在 Render Function 場(chǎng)景下需要手動(dòng)導(dǎo)入:
import {
vModelText, // <input v-model>
vModelCheckbox, // <input type="checkbox" v-model>
vModelRadio, // <input type="radio" v-model>
vModelSelect, // <select v-model>
vModelDynamic, // 動(dòng)態(tài)類型的 v-model(根據(jù) input type 自動(dòng)選擇實(shí)現(xiàn))
vShow, // v-show
vOn, // v-on 事件監(jiān)聽
vBind, // v-bind
withDirectives, // 應(yīng)用自定義指令
} from '@vue/runtime-dom'
// 在 render function 中手動(dòng)使用 v-model
import { h, withDirectives, vModelText } from 'vue'
export default {
setup() {
const text = ref('')
return () => withDirectives(
h('input', { 'onUpdate:modelValue': val => (text.value = val) }),
[[vModelText, text.value]]
)
}
}
4.2 CSS 過渡鉤子(內(nèi)部實(shí)現(xiàn))
import { Transition, TransitionGroup } from '@vue/runtime-dom'
// TransitionGroup 配置詳解(文檔常被忽視的屬性)
h(TransitionGroup, {
name: 'list',
tag: 'ul', // 渲染的包裝元素(默認(rèn) span)
moveClass: 'move', // FLIP 動(dòng)畫時(shí)的 CSS 類名
appear: true, // 初次渲染也觸發(fā)過渡
css: false, // 禁用 CSS 過渡,純 JS 控制
onBeforeEnter(el) {},
onEnter(el, done) { /* 必須調(diào)用 done() */ },
onLeave(el, done) { /* 必須調(diào)用 done() */ },
})
4.3 SSR 相關(guān)
import { createSSRApp } from '@vue/runtime-dom'
// SSR hydration:將服務(wù)端渲染的 HTML 與 Vue 狀態(tài)綁定
const app = createSSRApp(App)
app.mount('#app') // 自動(dòng)檢測(cè)已有 HTML,進(jìn)行 hydration 而非全量渲染
五、@vue/compiler-core:編譯器核心
這是 Vue 模板編譯管道的核心,純函數(shù)式架構(gòu),完全平臺(tái)無關(guān)。
5.1 完整編譯管道
模板字符串
↓
parse() → AST(抽象語法樹)
↓
transform() → 轉(zhuǎn)換 AST(應(yīng)用各種 transform 插件)
↓
generate() → 渲染函數(shù)代碼字符串
import { parse, transform, generate, baseParse } from '@vue/compiler-core'
// 解析模板為 AST
const ast = parse('<div>{{ msg }}</div>')
// 對(duì) AST 進(jìn)行轉(zhuǎn)換(插件化)
transform(ast, {
nodeTransforms: [
// 自定義 transform 插件
(node, context) => {
if (node.type === 1 /* ELEMENT */ && node.tag === 'div') {
// 可以修改 AST,添加、刪除、替換節(jié)點(diǎn)
}
}
]
})
// 生成代碼
const { code } = generate(ast)
console.log(code) // 輸出渲染函數(shù)字符串
5.2 AST 節(jié)點(diǎn)類型(NodeTypes枚舉)
import { NodeTypes } from '@vue/compiler-core'
// 完整節(jié)點(diǎn)類型列表(調(diào)試 AST 時(shí)極為有用)
NodeTypes.ROOT // 0 - 根節(jié)點(diǎn)
NodeTypes.ELEMENT // 1 - 元素節(jié)點(diǎn)
NodeTypes.TEXT // 2 - 文本節(jié)點(diǎn)
NodeTypes.COMMENT // 3 - 注釋節(jié)點(diǎn)
NodeTypes.SIMPLE_EXPRESSION // 4 - 簡(jiǎn)單表達(dá)式(如 msg、count + 1)
NodeTypes.INTERPOLATION // 5 - 插值({{ }})
NodeTypes.ATTRIBUTE // 6 - 普通屬性
NodeTypes.DIRECTIVE // 7 - 指令(v-if、v-for 等)
NodeTypes.COMPOUND_EXPRESSION // 8 - 復(fù)合表達(dá)式
NodeTypes.IF // 9 - v-if 結(jié)構(gòu)
NodeTypes.IF_BRANCH // 10 - v-if 分支
NodeTypes.FOR // 11 - v-for 結(jié)構(gòu)
NodeTypes.TEXT_CALL // 12
NodeTypes.VNODE_CALL // 13 - createVNode 調(diào)用
NodeTypes.JS_CALL_EXPRESSION // 14
NodeTypes.JS_OBJECT_EXPRESSION // 15
NodeTypes.JS_PROPERTY // 16
NodeTypes.JS_ARRAY_EXPRESSION // 17
NodeTypes.JS_FUNCTION_EXPRESSION // 18
NodeTypes.JS_CONDITIONAL_EXPRESSION // 19
NodeTypes.JS_CACHE_EXPRESSION // 20 - 帶緩存的表達(dá)式(v-once / 靜態(tài)提升)
5.3 編譯優(yōu)化標(biāo)記(PatchFlags)
這是 Vue 3 性能優(yōu)化的核心機(jī)制,編譯器通過這些標(biāo)記讓運(yùn)行時(shí)的 diff 算法跳過靜態(tài)內(nèi)容:
import { PatchFlags } from '@vue/compiler-core'
PatchFlags.TEXT // 1 - 動(dòng)態(tài)文本內(nèi)容
PatchFlags.CLASS // 2 - 動(dòng)態(tài) class
PatchFlags.STYLE // 4 - 動(dòng)態(tài) style
PatchFlags.PROPS // 8 - 動(dòng)態(tài) props(非 class/style)
PatchFlags.FULL_PROPS // 16 - 有動(dòng)態(tài)鍵名的 props(如 v-bind="obj")
PatchFlags.HYDRATE_EVENTS // 32 - 含事件監(jiān)聽的節(jié)點(diǎn)(SSR hydration 用)
PatchFlags.STABLE_FRAGMENT // 64 - 子節(jié)點(diǎn)順序穩(wěn)定的 Fragment
PatchFlags.KEYED_FRAGMENT // 128 - 帶 key 的 Fragment
PatchFlags.UNKEYED_FRAGMENT // 256 - 無 key 的 Fragment(v-for 無 key 時(shí))
PatchFlags.NEED_PATCH // 512 - 需要 patch 但不在上述分類中
PatchFlags.DYNAMIC_SLOTS // 1024 - 動(dòng)態(tài)插槽
PatchFlags.DEV_ROOT_FRAGMENT // 2048 - dev 模式根節(jié)點(diǎn) Fragment
PatchFlags.HOISTED // -1 - 靜態(tài)提升節(jié)點(diǎn),永不更新
PatchFlags.BAIL // -2 - 退出優(yōu)化,進(jìn)行完整 diff
理解 PatchFlags 是分析 Vue 模板編譯產(chǎn)物、進(jìn)行極致性能優(yōu)化的必備知識(shí)。
5.4ShapeFlags(組件形態(tài)標(biāo)記)
import { ShapeFlags } from '@vue/shared' // 實(shí)際在 shared 包中
ShapeFlags.ELEMENT // 1 - 普通 DOM 元素
ShapeFlags.FUNCTIONAL_COMPONENT // 2 - 函數(shù)式組件
ShapeFlags.STATEFUL_COMPONENT // 4 - 有狀態(tài)組件
ShapeFlags.TEXT_CHILDREN // 8 - children 是文本
ShapeFlags.ARRAY_CHILDREN // 16 - children 是數(shù)組
ShapeFlags.SLOTS_CHILDREN // 32 - children 是插槽對(duì)象
ShapeFlags.TELEPORT // 64 - Teleport 組件
ShapeFlags.SUSPENSE // 128 - Suspense 組件
ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE // 256
ShapeFlags.COMPONENT_KEPT_ALIVE // 512
ShapeFlags.COMPONENT // 6 - FUNCTIONAL | STATEFUL 的組合
5.5 自定義編譯器插件
import { parse, transform, generate, createTransformContext } from '@vue/compiler-core'
// 實(shí)現(xiàn)一個(gè)自定義 transform:自動(dòng)給所有 div 添加 data-testid
const myTransform = (node, context) => {
if (node.type === 1 && node.tag === 'div') {
node.props.push({
type: 6, // NodeTypes.ATTRIBUTE
name: 'data-testid',
value: { type: 2, content: 'auto-id' }
})
}
}
const ast = parse('<div><span>hello</span></div>')
transform(ast, { nodeTransforms: [myTransform] })
const { code } = generate(ast, { mode: 'module' })
六、@vue/compiler-dom:瀏覽器編譯器
在 compiler-core 基礎(chǔ)上擴(kuò)展了瀏覽器專屬的處理邏輯。
6.1@vue/compiler-domvs@vue/compiler-core
| 能力 | compiler-core | compiler-dom |
|---|---|---|
| 模板解析 | ? | ?(擴(kuò)展 HTML 實(shí)體) |
| v-if / v-for | ? | ? |
| v-model | 基礎(chǔ)實(shí)現(xiàn) | ?(區(qū)分 input/select/checkbox) |
| v-on | 基礎(chǔ)實(shí)現(xiàn) | ?(支持 .stop .prevent 等修飾符) |
| 靜態(tài)提升 | ? | ? |
| 內(nèi)聯(lián)事件緩存 | ? | ? |
| SSR 優(yōu)化 | ? | ? |
6.2compile函數(shù)
import { compile } from '@vue/compiler-dom'
// 直接將模板字符串編譯為渲染函數(shù)代碼
const { code } = compile('<div>{{ msg }}</div>', {
mode: 'module', // 'module' | 'function'
prefixIdentifiers: true, // 是否給變量名加前綴
hoistStatic: true, // 靜態(tài)節(jié)點(diǎn)提升
cacheHandlers: true, // 內(nèi)聯(lián)事件處理器緩存
ssr: false, // 是否生成 SSR 渲染代碼
ssrCssVars: '', // SSR CSS 變量
isNativeTag: (tag) => true, // 判斷是否是原生 HTML 標(biāo)簽
isCustomElement: (tag) => tag.includes('-'), // 自定義元素判斷
// 編譯時(shí)優(yōu)化選項(xiàng)
scopeId: 'data-v-xxxxxx', // Scoped CSS 的 scope ID(SFC 場(chǎng)景)
})
console.log(code)
// import { toDisplayString as _toDisplayString, ... } from "vue"
// export function render(_ctx, _cache) {
// return (_openBlock(), _createElementBlock("div", null, _toDisplayString(_ctx.msg), ...))
// }
七、@vue/shared:內(nèi)部工具函數(shù)
這個(gè)包雖然標(biāo)注為"內(nèi)部使用",但其中有不少工具函數(shù)在框架級(jí)開發(fā)中很有價(jià)值。
import {
EMPTY_OBJ, // Object.freeze({}) — 空對(duì)象常量
EMPTY_ARR, // Object.freeze([]) — 空數(shù)組常量
NOOP, // () => {} — 空操作函數(shù)
NO, // () => false
isArray, // Array.isArray
isMap, // 判斷是否為 Map
isSet, // 判斷是否為 Set
isDate, // 判斷是否為 Date
isRegExp, // 判斷是否為 RegExp
isFunction, // typeof val === 'function'
isString, // typeof val === 'string'
isSymbol, // typeof val === 'symbol'
isObject, // val !== null && typeof val === 'object'
isPromise, // isObject(val) && isFunction(val.then)
isPlainObject, // Object.prototype.toString.call(val) === '[object Object]'
extend, // Object.assign
hasOwn, // Object.prototype.hasOwnProperty.call
camelize, // kebab-case → camelCase('on-click' → 'onClick')
capitalize, // 首字母大寫
hyphenate, // camelCase → kebab-case(與 camelize 互逆)
toHandlerKey, // 'click' → 'onClick'
looseEqual, // 寬松相等比較(數(shù)組、對(duì)象深比較,用于 v-model 多選場(chǎng)景)
looseIndexOf, // 使用 looseEqual 的 indexOf
invokeArrayFns, // 批量調(diào)用函數(shù)數(shù)組(生命周期鉤子的內(nèi)部調(diào)用方式)
def, // Object.defineProperty 封裝
toRawType, // Object.prototype.toString 取類型名('Map', 'Set', 'Array' 等)
makeMap, // 從字符串創(chuàng)建成員檢測(cè)函數(shù)(HTML 標(biāo)簽白名單等場(chǎng)景)
} from '@vue/shared'
// makeMap 示例:
const isHTMLTag = makeMap('div,span,p,a,img,ul,li,...')
isHTMLTag('div') // true
isHTMLTag('my-comp') // false
八、完整包導(dǎo)出結(jié)構(gòu)總覽
Vue 3 包生態(tài)
│
├── @vue/reactivity(獨(dú)立響應(yīng)式)
│ ├── 核心:ref, reactive, computed, watch
│ ├── 淺層:shallowRef, shallowReactive, shallowReadonly
│ ├── 副作用:effect, effectScope, getCurrentScope, onScopeDispose
│ ├── 追蹤控制:pauseTracking, resumeTracking, resetTracking
│ ├── 工具:toRaw, markRaw, proxyRefs, triggerRef, customRef
│ ├── 類型判斷:isRef, isReactive, isReadonly, isProxy, isShallow
│ └── 轉(zhuǎn)換:toRef, toRefs, toValue, unref
│
├── @vue/runtime-core(核心運(yùn)行時(shí))
│ ├── 應(yīng)用:createApp(通過 runtime-dom 暴露)
│ ├── 組件:defineComponent, defineAsyncComponent
│ ├── VNode:h, createVNode, cloneVNode, mergeProps, createTextVNode
│ ├── 編譯助手:openBlock, createBlock, createElementBlock(內(nèi)部)
│ ├── 實(shí)例:getCurrentInstance, useAttrs, useSlots, useCssModule
│ ├── 注入:provide, inject, InjectionKey
│ ├── 生命周期:onMounted...onUnmounted, onRenderTracked, onRenderTriggered
│ ├── 調(diào)度:nextTick, queuePostFlushCb
│ ├── 渲染器:createRenderer, createHydrationRenderer
│ └── 內(nèi)置組件:Suspense, KeepAlive, Teleport(定義在此)
│
├── @vue/runtime-dom(瀏覽器運(yùn)行時(shí))
│ ├── 應(yīng)用:createApp, createSSRApp, render, hydrate
│ ├── 指令實(shí)現(xiàn):vModelText, vModelCheckbox, vShow 等
│ ├── 過渡:Transition, TransitionGroup
│ └── 重新導(dǎo)出:全部 runtime-core 導(dǎo)出
│
├── @vue/compiler-core(編譯器核心)
│ ├── 編譯管道:parse, transform, generate
│ ├── 枚舉:NodeTypes, PatchFlags, ElementTypes
│ ├── 工具:createTransformContext, traverseNode
│ └── 助手:createSimpleExpression, createCallExpression 等
│
├── @vue/compiler-dom(瀏覽器編譯器)
│ ├── 頂層:compile(模板字符串 → 渲染函數(shù)代碼)
│ └── 重新導(dǎo)出:全部 compiler-core 導(dǎo)出
│
└── @vue/shared(內(nèi)部工具)
├── 類型判斷:isArray, isFunction, isObject, isPromise...
├── 字符串:camelize, hyphenate, capitalize, toHandlerKey
├── 標(biāo)志:ShapeFlags, PatchFlags(部分)
└── 工具:makeMap, extend, hasOwn, looseEqual, invokeArrayFns九、架構(gòu)層次與使用建議
| 包 | 適用場(chǎng)景 | 是否可獨(dú)立使用 |
|---|---|---|
@vue/reactivity | 非 UI 的響應(yīng)式狀態(tài)管理 | ? 完全獨(dú)立 |
@vue/runtime-core | 自定義渲染器 | ? 需搭配自定義渲染器 |
@vue/runtime-dom | 瀏覽器 Vue 應(yīng)用 | ? 等價(jià)于 vue(無編譯器) |
@vue/compiler-core | 構(gòu)建工具插件、代碼轉(zhuǎn)換 | ? 獨(dú)立使用 |
@vue/compiler-dom | 運(yùn)行時(shí)模板編譯(少用) | ? 獨(dú)立使用 |
@vue/shared | 框架級(jí)工具函數(shù) | ?? 內(nèi)部包,API 隨版本變化 |
vue | 標(biāo)準(zhǔn)應(yīng)用開發(fā) | ? 聚合所有能力 |
十、總結(jié)
Vue 3 的包體系圍繞關(guān)注點(diǎn)分離構(gòu)建:
- 響應(yīng)式 與 渲染 完全解耦,
@vue/reactivity可獨(dú)立運(yùn)行 - 平臺(tái)無關(guān)運(yùn)行時(shí) 與 瀏覽器平臺(tái)實(shí)現(xiàn) 分離,
createRenderer支持跨平臺(tái) - 編譯器 同樣分層,
compiler-core可擴(kuò)展用于 Vite 插件、Babel 轉(zhuǎn)換等 PatchFlags和effectScope等低層 API 是框架性能優(yōu)化的真正秘密
理解這套架構(gòu),不僅能寫出更高質(zhì)量的組合函數(shù)和 UI 組件庫,也能在需要時(shí)深入框架層進(jìn)行擴(kuò)展——這是從"會(huì)用 Vue"到"精通 Vue 架構(gòu)"的關(guān)鍵跨越。
到此這篇關(guān)于Vue包之vue各子包究竟導(dǎo)出了什么的文章就介紹到這了,更多相關(guān)vue包導(dǎo)出什么內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3使用axios并封裝axios請(qǐng)求的詳細(xì)步驟
本篇文章分步驟給大家介紹了vue3使用axios并封裝axios請(qǐng)求的詳細(xì)步驟,結(jié)合實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友參考下吧2023-06-06
利用vite創(chuàng)建vue3項(xiàng)目的全過程及一個(gè)小BUG詳解
Vite作為一個(gè)構(gòu)建工具,提供了一種快速的方法來構(gòu)建Vue應(yīng)用,而Vue3?則是一個(gè)前端框架,提供了強(qiáng)大的功能來構(gòu)建和管理前端項(xiàng)目,下面這篇文章主要給大家介紹了關(guān)于利用vite創(chuàng)建vue3項(xiàng)目的全過程及一個(gè)小BUG的相關(guān)資料,需要的朋友可以參考下2023-04-04
vue element-ui實(shí)現(xiàn)el-table表格多選以及回顯方式
這篇文章主要介紹了vue element-ui實(shí)現(xiàn)el-table表格多選以及回顯方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
Vue利用mockjs編寫假數(shù)據(jù)并應(yīng)用的問題記錄
這篇文章主要介紹了Vue利用mockjs編寫假數(shù)據(jù)并應(yīng)用,本文通過實(shí)例代碼給大家詳細(xì)講解,對(duì)Vue?mockjs數(shù)據(jù)相關(guān)知識(shí)感興趣的朋友跟隨小編一起看看吧2022-12-12
Vue監(jiān)聽數(shù)據(jù)對(duì)象變化源碼
這篇文章主要為大家詳細(xì)介紹了Vue監(jiān)聽數(shù)據(jù)對(duì)象變化的源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03

