Vue 3 全面总结
目录
1. 响应式系统
1.1 ref - 基础引用
ref() 用于创建响应式引用,可包含任何类型的值。
javascript
import { ref } from 'vue'
// 原始值
const count = ref(0)
const message = ref('Hello')
// 访问和修改需要通过 .value
count.value++
console.log(count.value) // 1
// 对象类型(仍然是浅响应)
const user = ref({ name: 'John', age: 30 })
user.value.age = 31
// 模板中自动解包,不需要 .value
// <div>{{ count }}</div>1.2 reactive - 深度响应式对象
reactive() 创建一个深度响应式的对象代理。
javascript
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'John',
profile: { age: 30 }
}
})
// 直接访问,不需要 .value
state.count++
state.user.profile.age = 31
// 深度响应式 - 嵌套对象也会变成响应式重要注意事项:
javascript
// ❌ 解构会丢失响应性
const { count } = state // count 不再是响应式的
// ✅ 使用 toRefs 保持响应性
import { toRefs } from 'vue'
const { count, user } = toRefs(state) // 保持响应连接1.3 computed - 计算属性
创建缓存的、派生的响应式值。
javascript
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// 只读计算属性(自动缓存)
const fullName = computed(() => {
console.log('Computing...') // 只在依赖变化时执行
return `${firstName.value} ${lastName.value}`
})
console.log(fullName.value) // "John Doe"
console.log(fullName.value) // 缓存,不重新计算
firstName.value = 'Jane'
console.log(fullName.value) // "Jane Doe" - 重新计算
// 可写计算属性
const fullNameWritable = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(newValue) {
const [first, last] = newValue.split(' ')
firstName.value = first
lastName.value = last || ''
}
})
fullNameWritable.value = 'Alice Smith'1.4 watch 与 watchEffect
watch - 精确监听
javascript
import { ref, watch } from 'vue'
const count = ref(0)
// 监听单个 ref
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
// 监听多个源
const name = ref('John')
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log('Multiple sources changed')
})
// 监听计算属性或 getter
watch(
() => count.value * 2,
(newValue, oldValue) => {
console.log('Derived value changed')
}
)
// 选项配置
watch(count, (newValue, oldValue) => {
console.log('Changed')
}, {
immediate: true, // 立即执行回调
deep: true, // 深度监听对象/数组
flush: 'post' // 'pre' | 'post' | 'sync'
})watchEffect - 自动追踪依赖
javascript
import { ref, watchEffect } from 'vue'
const count = ref(0)
// 立即执行并自动追踪所有访问的响应式数据
const stop = watchEffect((onCleanup) => {
console.log(`Count is: ${count.value}`)
// 清理函数
onCleanup(() => {
console.log('Effect cleanup')
})
})
count.value++ // 自动触发
// 停止监听
stop()watch vs watchEffect 对比:
| 特性 | watch | watchEffect |
|---|---|---|
| 依赖追踪 | 显式指定 | 自动追踪 |
| 初始执行 | 需要 immediate | 立即执行 |
| 接收旧值 | 是 | 否 |
| 性能控制 | 更精确 | 可能追踪多余依赖 |
2. 组合式 API (Composition API)
2.1 setup 函数
javascript
import { defineComponent, ref } from 'vue'
export default defineComponent({
props: {
title: String,
count: {
type: Number,
default: 0
}
},
emits: ['increment', 'decrement'],
setup(props, context) {
// props 是只读的响应式对象
console.log(props.title)
// context 包含:attrs, slots, emit
const { emit, slots, attrs } = context
// 暴露给模板
return {
message: ref('Hello'),
increment() {
emit('increment')
}
}
}
})2.2 script setup 语法糖
推荐使用的简洁语法:
vue
<script setup>
import { ref, computed, watch } from 'vue'
// Props - 声明式
defineProps({
title: String,
count: {
type: Number,
default: 0
}
})
// Props - 类型化(需 TypeScript)
// defineProps<{ title: string; count?: number }>()
// Emits
defineEmits(['increment', 'decrement'])
// 所有顶层变量自动暴露给模板
const message = ref('Hello')
const double = computed(() => props.count * 2)
// 方法
function handleClick() {
console.log('Clicked')
}
// 模板插槽
const slots = defineSlots()
// 具名插槽暴露
defineOptions({
name: 'MyComponent'
})
</script>
<template>
<div @click="handleClick">
<h1>{{ title }}</h1>
<p>{{ message }}</p>
<p>{{ double }}</p>
</div>
</template>2.3 Composables (组合函数)
可复用的组合式逻辑:
javascript
// useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const double = computed(() => count.value * 2)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = initialValue
return {
count,
double,
increment,
decrement,
reset
}
}
// 在组件中使用
<script setup>
import { useCounter } from './useCounter'
const { count, double, increment, decrement } = useCounter(10)
</script>3. 生命周期钩子
3.1 Composition API 生命周期
javascript
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
onActivated,
onDeactivated
} from 'vue'
export default {
setup() {
const count = ref(0)
// beforeCreate - setup 之前,无法访问
// created - setup 函数本身相当于 created
onBeforeMount(() => {
console.log('挂载前 - DOM 尚未准备')
})
onMounted(() => {
console.log('已挂载 - DOM 就绪')
// 可以安全访问 DOM
})
onBeforeUpdate(() => {
console.log('更新前 - 状态已变,DOM 未更新')
})
onUpdated(() => {
console.log('已更新 - DOM 反映新状态')
})
onBeforeUnmount(() => {
console.log('卸载前 - 清理定时器、监听器')
})
onUnmounted(() => {
console.log('已卸载 - 组件从 DOM 移除')
})
// 错误捕获(捕获子组件错误)
onErrorCaptured((err, instance, info) => {
console.error('子组件错误:', err, info)
return false // 阻止错误向上传播
})
// KeepAlive 专属
onActivated(() => {
console.log('从缓存激活')
})
onDeactivated(() => {
console.log('失活到缓存')
})
return { count }
}
}3.2 生命周期时序图
[父 onBeforeMount] → [子 onBeforeMount] → [子 onMounted] → [父 onMounted]
↓
[状态变化] → [onBeforeUpdate] → [DOM 更新] → [onUpdated]
↓
[卸载] → [onBeforeUnmount] → [子卸载] → [onUnmounted]4. 核心内置组件
4.1 Transition - 过渡动画
vue
<template>
<!-- 元素显示/隐藏过渡 -->
<Transition name="fade">
<div v-if="show">内容</div>
</Transition>
<!-- 动态组件过渡 -->
<Transition name="fade" mode="out-in" appear>
<component :is="currentView" />
</Transition>
<!-- 列表项过渡 -->
<TransitionGroup tag="ul" name="slide">
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</TransitionGroup>
</template>
<style>
/* CSS 类名过渡 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* 列表过渡 */
.slide-move,
.slide-enter-active,
.slide-leave-active {
transition: all 0.5s ease;
}
.slide-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.slide-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>Transition Props:
| Prop | 说明 |
|---|---|
| name | 过渡名称 |
| appear | 初始化时也应用过渡 |
| mode | 模式:in-out | out-in | default |
| css | 是否使用 CSS 过渡 |
| duration | 持续时间 |
| enter-from-class | 进入起始类名 |
| leave-active-class | 离开激活类名 |
4.2 Teleport - 传送门
将内容渲染到 DOM 其他位置:
vue
<template>
<!-- 传送到 #app 下的 #modal-container -->
<Teleport to="#modal-container">
<div class="modal">
<p>模态框内容</p>
<button @click="close">关闭</button>
</div>
</Teleport>
<!-- 条件禁用传送 -->
<Teleport to="#popup" :disabled="displayVideoInline">
<video src="./movie.mp4" />
</Teleport>
<!-- Vue 3.5+ 延迟解析目标 -->
<Teleport defer to="#late-div">
内容
</Teleport>
<!-- 稍后定义的目标 -->
<div id="late-div"></div>
</template>4.3 KeepAlive - 组件缓存
缓存组件实例以保留状态:
vue
<template>
<KeepAlive :include="['Home', 'Settings']" :exclude="['About']">
<component :is="currentComponent" />
</KeepAlive>
</template>
<script setup>
import { ref } from 'vue'
const currentComponent = ref('Home')
</script>KeepAlive Props:
| Prop | 说明 |
|---|---|
| include | 缓存的组件名(字符串/正则/数组) |
| exclude | 不缓存的组件名 |
| max | 最大缓存数量 |
配合生命周期使用:
javascript
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
console.log('组件被显示(从缓存恢复)')
// 重新获取数据等
})
onDeactivated(() => {
console.log('组件被隐藏(保存到缓存)')
// 暂停轮询等
})4.4 Suspense - 异步依赖
处理异步组件加载:
vue
<template>
<Suspense timeout="1000">
<!-- 默认内容 -->
<template #default>
<AsyncComponent />
</template>
<!-- 加载中回退 -->
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>Suspense Events:
vue
<Suspense
@pending="onPending"
@resolve="onResolve"
@fallback="onFallback"
>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>5. 自定义指令
5.1 注册方式
javascript
import { createApp } from 'vue'
const app = createApp({})
// 全局注册
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// 局部注册
export default {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}5.2 指令钩子
javascript
const exampleDirective = {
// 绑定只调用一次
created(binding, vnode) {
console.log('指令绑定,元素还未插入')
},
// 元素插入 DOM
beforeMount(binding, vnode) {
console.log('元素插入前')
},
// 元素插入 DOM 后
mounted(binding, vnode) {
console.log('元素已插入')
// 常用:操作 DOM
},
// 父组件 VNode 更新前
beforeUpdate(binding, vnode, prevVnode) {
console.log('父组件更新前')
},
// 父组件及其子组件 VNode 都更新后
updated(binding, vnode, prevVnode) {
console.log('父组件更新后')
},
// 元素卸载前
beforeUnmount(binding, vnode) {
console.log('元素卸载前')
// 清理工作
},
// 元素及子树卸载后
unmounted(binding, vnode) {
console.log('元素已卸载')
}
}5.3 binding 对象
javascript
const myDirective = {
mounted(el, binding) {
console.log(binding.name) // 'my'
console.log(binding.value) // 指令值
console.log(binding.arg) // 参数,v-el:year 中的 year
console.log(binding.modifiers) // 修饰符对象 { focus: true }
console.log(binding.instance) // ❌ Vue 3 不再提供
}
}5.4 简写形式
javascript
// 函数形式 - 等价于 mounted 和 updated
app.directive('color', (el, binding) => {
el.style.color = binding.value
})
// 使用
<div v-color="'red'">红色文本</div>
<div v-color:bg="'blue'">蓝色背景</div>5.5 实战示例
javascript
// 防抖指令
app.directive('debounce', {
mounted(el, binding) {
let timer = null
const handler = (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
binding.value(...args)
}, binding.arg || 300)
}
el.addEventListener('click', handler)
el._unregister = () => {
el.removeEventListener('click', handler)
clearTimeout(timer)
}
},
unmounted(el) {
el._unregister?.()
}
})
// 使用
<button v-debounce:500="handleClick">点击</button>6. 依赖注入
6.1 provide / inject 基础
vue
<!-- 父组件 -->
<script setup>
import { provide, ref, readonly } from 'vue'
const theme = ref('light')
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
// 提供响应式数据(建议用 readonly 保护)
provide('theme', readonly(theme))
provide('toggleTheme', toggleTheme)
// 提供普通值
provide('appName', 'My App')
</script>vue
<!-- 子组件(任意层级) -->
<script setup>
import { inject } from 'vue'
// 注入
const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
// 带默认值
const appName = inject('appName', 'Default App')
// 必须传入符号表示非必填,默认值为函数
const config = inject('config', () => ({ debug: false }), true)
</script>6.2 类型安全的 InjectionKey
typescript
// types.ts
import { InjectionKey, Ref } from 'vue'
export interface ThemeContext {
theme: Ref<string>
toggleTheme: () => void
}
export const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme')typescript
// Provider.vue
import { provide, readonly } from 'vue'
import { ThemeKey } from './types'
provide(ThemeKey, {
theme: readonly(theme),
toggleTheme
})
// Consumer.vue
import { inject } from 'vue'
import { ThemeKey } from './types'
const themeContext = inject(ThemeKey)
// themeContext.theme.value 有类型提示6.3 实战:权限控制
javascript
// usePermission.js
import { provide, inject, ref, computed } from 'vue'
const PermissionKey = Symbol('permission')
export function usePermissionProvider(roles) {
const userRoles = ref(roles)
const hasPermission = (requiredRole) => {
return userRoles.value.includes(requiredRole)
}
provide(PermissionKey, {
roles: readonly(userRoles),
hasPermission
})
}
export function usePermission() {
const perms = inject(PermissionKey)
if (!perms) {
throw new Error('Permission not provided!')
}
return perms
}
// 使用
// 根组件
usePermissionProvider(['admin', 'user'])
// 子组件
const { hasPermission } = usePermission()
if (hasPermission('admin')) {
// 显示管理员功能
}7. 渲染函数与虚拟 DOM
7.1 h 函数基础
javascript
import { h } from 'vue'
// h(type, props, children)
// 创建元素
const div = h('div', { class: 'container' }, 'Hello World')
// 多个子节点
const list = h('ul', { id: 'my-list' }, [
h('li', 'Item 1'),
h('li', 'Item 2'),
h('li', 'Item 3')
])
// 事件处理
const button = h('button', {
class: 'btn',
onClick: () => console.log('clicked')
}, 'Click me')7.2 纯 render 函数组件
javascript
import { h, defineComponent, ref } from 'vue'
const Counter = defineComponent({
setup() {
const count = ref(0)
return () => {
return h('div', { class: 'counter' }, [
h('p', `Count: ${count.value}`),
h('button', {
onClick: () => count.value++
}, 'Increment'),
h('button', {
onClick: () => count.value--
}, 'Decrement')
])
}
}
})7.3 组件作为子节点
javascript
import { h, defineComponent } from 'vue'
const Child = defineComponent({
props: ['message'],
setup(props) {
return () => h('div', props.message)
}
})
const Parent = defineComponent({
setup() {
return () => h('div', [
h('h1', 'Parent Component'),
h(Child, { message: 'Hello from parent' }),
h(Child, { message: 'Another child' })
])
}
})7.4 插槽实现
javascript
const Card = defineComponent({
setup(props, { slots }) {
return () => h('div', { class: 'card' }, [
h('header', { class: 'card-header' }, slots.header?.()),
h('main', { class: 'card-body' }, slots.default?.()),
h('footer', { class: 'card-footer' }, slots.footer?.())
])
}
})
// 使用(render 函数中)
h(Card, {}, {
header: () => h('h3', 'Title'),
default: () => h('p', 'Content'),
footer: () => h('span', 'Footer')
})7.5 虚拟 DOM 结构
javascript
// VNode 基本结构
{
__v_isVNode: true,
type: 'div', // 元素类型或组件
props: { class: 'foo' },
key: 'unique-key',
ref: someRef,
children: ['text'], // 子节点
suspense: null,
ssContent: null,
ssFallback: null,
scopeId: null,
slotScopeIds: null,
axis: undefined
}7.6 createVNode
javascript
import { createVNode, render } from 'vue'
// 手动创建并挂载 VNode
const root = document.getElementById('app')
const vnode = createVNode('div', { class: 'hello' }, 'Hello Vue 3')
render(vnode, root)8. 高级响应式工具
8.1 shallowRef / shallowReactive
浅层响应式,只追踪第一层:
javascript
import { shallowRef, shallowReactive } from 'vue'
// shallowRef - 只追踪 .value 变化
const component = shallowRef({
nested: { value: 1 }
})
component.value.nested.value = 2 // ❌ 不触发
component.value = {} // ✅ 触发
// shallowReactive - 只追踪对象第一层
const state = shallowReactive({
nested: { count: 0 }
})
state.nested.count++ // ❌ 不触发
state.nested = {} // ✅ 触发
// 适用场景:大型不可变数据结构、第三方库实例8.2 toRef / toRefs
保持解构时的响应性:
javascript
import { reactive, toRefs, toRef } from 'vue'
const state = reactive({
firstName: 'John',
lastName: 'Doe',
age: 30
})
// toRefs - 转换所有属性为 ref
const { firstName, lastName, age } = toRefs(state)
firstName.value = 'Jane' // state.firstName 也变为 'Jane'
// toRef - 单个属性
const nameRef = toRef(state, 'firstName')
nameRef.value = 'Bob' // state.firstName 也变为 'Bob'
// toRef - 带默认值
const nickname = toRef(state, 'nickname', 'Unknown')
// toRef - 计算 getter
const fullName = toRef(() => `${state.firstName} ${state.lastName}`)8.3 readonly / shallowReadonly
创建不可变代理:
javascript
import { reactive, readonly, shallowReadonly } from 'vue'
const original = reactive({
count: 0,
nested: { value: 1 }
})
// readonly - 深度只读
const copy = readonly(original)
// copy.count++ // ❌ 警告
// copy.nested.value++ // ❌ 警告
original.count++
console.log(copy.count) // 1 - 仍然反映原值变化
// shallowReadonly - 只读第一层
const shallowCopy = shallowReadonly(original)
// shallowCopy.count++ // ❌ 警告
shallowCopy.nested.value++ // ✅ 允许(内层不是只读)8.4 markRaw
标记对象永不响应式:
javascript
import { reactive, markRaw } from 'vue'
const state = reactive({
mutable: { foo: 1 },
immutable: markRaw({ bar: 2 })
})
state.mutable.foo = 2 // ✅ 响应式
state.immutable.bar = 3 // ✅ 但不会触发响应式更新
// 适用场景:
// 1. 不应该转为代理的性能敏感对象
// 2. 第三方类实例
// 3. 已经自带响应式系统的对象8.5 customRef
自定义 ref 的追踪和触发逻辑:
javascript
import { customRef } from 'vue'
function useDebouncedRef(value, delay = 200) {
let timer = null
return customRef((track, trigger) => {
let currentValue = value
return {
get() {
track()
return currentValue
},
set(newValue) {
clearTimeout(timer)
timer = setTimeout(() => {
currentValue = newValue
trigger()
}, delay)
}
}
})
}
// 使用
const debouncedSearch = useDebouncedRef('', 500)
debouncedSearch.value = 'hello' // 500ms 后才触发更新8.6 v-memo 指令
Vue 3.2+ 手动缓存:
vue
<template>
<!-- 只在 searchQuery 变化时重新渲染 -->
<div v-memo="[searchQuery]">
<Item
v-for="item in filteredItems"
:key="item.id"
:search-query="searchQuery"
/>
</div>
<!-- 无 memo - 每次父组件更新都会重新渲染 -->
<div>
<Item v-for="item in items" :key="item.id" />
</div>
</template>9. 服务端渲染 (SSR)
9.1 基础 SSR
javascript
// server.js
import { createSSRApp, ref, h } from 'vue'
import { renderToString } from '@vue/server-renderer'
const App = {
setup() {
const msg = ref('Hello Server!')
return { msg }
},
template: `<div>{{ msg }}</div>`
}
async function render() {
const app = createSSRApp(App)
const html = await renderToString(app)
console.log(html) // '<div>Hello Server!</div>'
return html
}9.2 流式渲染
javascript
import { renderToNodeStream } from '@vue/server-renderer'
import { Readable } from 'stream'
// Node.js 流
function streamRender(res) {
const app = createSSRApp(App)
const stream = renderToNodeStream(app)
res.setHeader('Content-Type', 'text/html')
stream.pipe(res)
}
// Web Streams API
function webStreamRender() {
const app = createSSRApp(App)
const stream = renderToWebStream(app)
return new Response(stream, {
headers: { 'Content-Type': 'text/html' }
})
}9.3 SSR Context
javascript
import { renderToString, createSSRApp } from 'vue'
import { renderToNodeStream } from '@vue/server-renderer'
async function renderWithContext() {
const ctx = {}
const app = createSSRApp(App)
const html = await renderToString(app, ctx)
// ctx.teleports 包含 teleport 的内容
const teleports = ctx.teleports || {}
return `
<!DOCTYPE html>
<html>
<head>
${teleports.head || ''}
</head>
<body>
<div id="app">${html}</div>
${teleports.body || ''}
</body>
</html>
`
}9.4 Teleport 在 SSR 中
vue
<!-- 组件中使用 -->
<Teleport to="body">
<div class="modal">Modal content</div>
</Teleport>
<!-- SSR 输出会在 ctx.teleports 中收集 -->10. 底层原理
10.1 响应式系统原理
Vue 3 使用 ES6 Proxy 实现响应式:
javascript
// 简化版实现
const targetsMap = new WeakMap()
let activeEffect = null
function track(target, key) {
if (activeEffect) {
let effects = targetsMap.get(target)
if (!effects) {
effects = new Map()
targetsMap.set(target, effects)
}
let deps = effects.get(key)
if (!deps) {
deps = new Set()
effects.set(key, deps)
}
deps.add(activeEffect)
}
}
function trigger(target, key) {
const effects = targetsMap.get(target)
if (effects) {
const deps = effects.get(key)
if (deps) {
deps.forEach(effect => effect())
}
}
}
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key)
const value = target[key]
return typeof value === 'object' && value !== null
? reactive(value)
: value
},
set(target, key, value) {
target[key] = value
trigger(target, key)
return true
}
})
}
function effect(fn) {
const runner = () => {
activeEffect = runner
fn()
activeEffect = null
}
runner()
return runner
}
// 使用
const data = reactive({ count: 0 })
effect(() => {
console.log('count:', data.count)
})
data.count = 1 // 触发 effect10.2 ref 实现原理
javascript
function isRef(value) {
return !!value && value.__v_isRef === true
}
function ref(value) {
const refObject = {
__v_isRef: true,
get value() {
// 读取时追踪依赖
track(refObject, 'value')
return value
},
set value(newValue) {
if (newValue !== value) {
value = newValue
// 写入时触发更新
trigger(refObject, 'value')
}
}
}
return refObject
}
function unref(value) {
return isRef(value) ? value.value : value
}10.3 computed 实现原理
javascript
function computed(getter, setter) {
const objectRef = reactive({})
let result
let dirty = true
const effectFunc = () => {
result = getter()
dirty = false
}
effectFunc()
return Object.assign(() => {
if (dirty) {
effectFunc()
}
track(objectRef, 'value')
return result
}, {
get value() {
if (dirty) {
effectFunc()
}
track(objectRef, 'value')
return result
},
set value(val) {
setter?.(val)
},
__v_isRef: true,
_cacheEffect: effectFunc
})
}10.4 虚拟 DOM Diff 算法
Vue 3 的 Diff 算法优化:
1. 双端比较
- 头头比较
- 尾尾比较
- 头尾交叉比较
- 尾头交叉比较
2. Keyed 节点 Diff
- 使用新旧节点 key 映射
- 最长递增子序列优化
3. Patch Flags 优化
- TEXT: 只有文本变化
- CLASS: 只有 class 变化
- STYLE: 只有 style 变化
- PROPS: 只有 props 变化
- FULL_PROPS: 所有 props 可能变化javascript
// Patch Flags 示例
h('div', { class: 'foo' }, [-1 /* PATCH_KEYED_FRAGMENT */, [
h('span', { key: 'a' }, 'A'),
h('span', { key: 'b' }, 'B')
]])10.5 编译优化
Tree Shaking
javascript
// 只导入需要的 API
import { ref, computed } from 'vue'
// 未使用的 API 会被 tree-shaking 移除静态提升
vue
<!-- 源码 -->
<div><span>静态文本</span></div>
<!-- 编译后 -->
const _hoisted_1 = { class: "foo" }
const _hoisted_2 = resolveComponent('span')
const _hoisted_3 = ["静态文本"]
return () => h('div', _hoisted_1, [
h(_hoisted_2, null, _hoisted_3)
])补丁标志
vue
<!-- 源码 -->
<div :class="activeClass">{{ text }}</div>
<!-- 编译后带有 patchFlag -->
h('div', {
class: activeClass
}, text, 1 /* TEXT */)10.6 Fragment、Teleport、Suspense 处理
javascript
// Fragment - 多根节点
const vnode = createVNode(Fragment, null, [
createVNode('div', null, 'Header'),
createVNode('div', null, 'Main'),
createVNode('div', null, 'Footer')
])
// Teleport
const teleportVNode = createVNode(Teleport, { to: 'body' }, [
createVNode('div', null, 'Teleported content')
])
// Suspense
const suspenseVNode = createVNode(Suspense, null, {
default: createVNode(asyncComponent),
fallback: createVNode('div', null, 'Loading...')
})附录:常用 API 速查
响应式 API
| API | 说明 |
|---|---|
| ref | 创建响应式引用 |
| reactive | 创建响应式对象 |
| readonly | 创建只读代理 |
| shallowRef | 浅层响应式引用 |
| shallowReactive | 浅层响应式对象 |
| shallowReadonly | 浅层只读代理 |
| toRef | 创建属性 ref |
| toRefs | 转换为 refs 对象 |
| customRef | 自定义 ref |
| triggerRef | 触发 ref 更新 |
| unref | 解包 ref |
| isRef | 检查是否为 ref |
| isReactive | 检查是否响应式 |
| isReadonly | 检查是否只读 |
| isProxy | 检查是否为代理 |
| markRaw | 标记为非响应式 |
| toRaw | 获取原始对象 |
| effectScope | 创建作用域 |
| getCurrentScope | 获取当前作用域 |
| onScopeDispose | 作用域清理 |
计算与监视
| API | 说明 |
|---|---|
| computed | 计算属性 |
| watch | 精确监视 |
| watchEffect | 自动追踪监视 |
| watchPostEffect | 下一个 DOM 更新后运行 |
| watchSyncEffect | 同步运行 |
生命周期
| API | 说明 |
|---|---|
| onBeforeMount | 挂载前 |
| onMounted | 挂载后 |
| onBeforeUpdate | 更新前 |
| onUpdated | 更新后 |
| onBeforeUnmount | 卸载前 |
| onUnmounted | 卸载后 |
| onErrorCaptured | 错误捕获 |
| onActivated | 激活(KeepAlive) |
| onDeactivated | 失活(KeepAlive) |
| onServerPrefetch | 服务端预获取 |
组件相关
| API | 说明 |
|---|---|
| defineComponent | 定义组件 |
| defineProps | 定义 props(setup) |
| defineEmits | 定义 emits(setup) |
| defineExpose | 暴露属性 |
| defineOptions | 定义选项(setup) |
| defineSlots | 获取插槽(setup) |
| defineModel | 定义双向绑定(setup) |
| getCurrentInstance | 获取当前实例 |
| withDefaults | props 默认值 |
| withModifiers | 修饰事件 |
| withKeys | 键修饰符 |
工具函数
| API | 说明 |
|---|---|
| nextTick | 下一个 DOM 更新周期 |
| h | 创建 VNode |
| resolveComponent | 解析组件 |
| resolveDirective | 解析指令 |
| mergeProps | 合并 props |
| cloneVNode | 克隆 VNode |
| normalizeClass | 规范化 class |
| normalizeStyle | 规范化 style |
| openBlock | 开启块追踪 |
| setBlockTracking | 设置块追踪 |
| createElementBlock | 创建元素块 |
| createBlock | 创建块 |
文档生成日期:2026 年 4 月 15 日基于 Vue 3.x 官方文档整理
