2526 字
13 分钟
Vue3 核心知识点详解

Vue3 相比 Vue2 的主要改进#

Vue3 在性能、代码组织、类型支持等方面都有显著提升:

  • 性能提升:更快的渲染速度、更小的打包体积
  • Composition API:更好的代码组织和逻辑复用
  • 更好的 TypeScript 支持:完全使用 TypeScript 重写
  • 新特性:Teleport、Suspense、Fragment 等
  • 优化的响应式系统:基于 Proxy 的响应式实现

一、Composition API#

Composition API 是 Vue3 最重要的新特性之一,提供了更灵活的代码组织方式。

1. setup 函数#

setup 是 Composition API 的入口点,在组件创建之前执行。

<script setup>
import { ref, computed, onMounted } from 'vue'
// 响应式数据
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
// 方法
const increment = () => {
count.value++
}
// 生命周期
onMounted(() => {
console.log('组件已挂载')
})
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">增加</button>
</div>
</template>

2. 响应式 API#

ref - 基本类型响应式#

import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1

reactive - 对象响应式#

import { reactive } from 'vue'
const state = reactive({
count: 0,
message: 'Hello'
})
state.count++ // 直接访问,不需要 .value

computed - 计算属性#

import { ref, computed } from 'vue'
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
count.value++
console.log(plusOne.value) // 3

watch 和 watchEffect#

import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
// watch - 明确指定监听源
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
// watchEffect - 自动追踪依赖
watchEffect(() => {
console.log(`count is ${count.value}`)
})

3. 生命周期钩子#

import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
onBeforeMount(() => {
console.log('组件挂载前')
})
onMounted(() => {
console.log('组件已挂载')
})
onBeforeUpdate(() => {
console.log('组件更新前')
})
onUpdated(() => {
console.log('组件已更新')
})
onBeforeUnmount(() => {
console.log('组件卸载前')
})
onUnmounted(() => {
console.log('组件已卸载')
})

4. 逻辑复用 - Composables 自定义Hooks#

使用 Composables 提取和复用逻辑:

useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const doubleCount = computed(() => count.value * 2)
const increment = () => count.value++
const decrement = () => count.value--
return {
count,
doubleCount,
increment,
decrement
}
}
// 在组件中使用
import { useCounter } from './useCounter'
const { count, doubleCount, increment } = useCounter(10)

二、Vue3 编译优化#

Vue3 在编译阶段进行了多项优化,显著提升了运行时性能。

1. 静态提升(Static Hoisting)#

将静态节点提升到渲染函数的最外层,避免每次渲染时重新创建。

// 优化前
function render() {
return h('div', [
h('p', 'Hello'), // 静态节点,每次都重新创建
h('p', this.msg) // 动态节点
])
}
// 优化后
const _hoisted_1 = h('p', 'Hello') // 提升到外层,只创建一次
function render() {
return h('div', [
_hoisted_1, // 直接复用
h('p', this.msg)
])
}

2. 补丁标记(Patch Flags)#

Vue3 使用 PatchFlags 标记动态节点,在 diff 时只比较动态内容。

// 模板
<div>
<p>静态文本</p>
<p>{{ msg }}</p>
<p :class="cls">文本</p>
</div>
// 编译后(简化)
createVNode('div', null, [
createVNode('p', null, '静态文本'), // 无标记,完全静态
createVNode('p', null, msg, 1 /* TEXT */), // 标记:文本内容动态
createVNode('p', { class: cls }, '文本', 2 /* CLASS */) // 标记:class 动态
])

常见的 PatchFlags:

export const enum PatchFlags {
TEXT = 1, // 动态文本内容
CLASS = 2, // 动态 class
STYLE = 4, // 动态 style
PROPS = 8, // 动态属性(除了 class 和 style)
FULL_PROPS = 16, // 有动态 key 的属性
HYDRATE_EVENTS = 32, // 有事件监听器
STABLE_FRAGMENT = 64, // 稳定的 fragment
KEYED_FRAGMENT = 128, // 有 key 的 fragment
UNKEYED_FRAGMENT = 256, // 无 key 的 fragment
NEED_PATCH = 512, // 需要进行非 props 比较
DYNAMIC_SLOTS = 1024, // 动态插槽
HOISTED = -1, // 静态提升的节点
BAIL = -2 // 退出优化模式
}

3. 事件监听器缓存#

缓存事件监听器,避免每次渲染时重新创建。

<!-- 模板 -->
<button @click="count++">增加</button>
<!-- 编译后(简化) -->
<script>
const _cache = []
function render() {
return h('button', {
onClick: _cache[0] || (_cache[0] = ($event) => count++)
})
}
</script>

4. Tree Shaking 优化#

Vue3 的 API 都是按需引入的,未使用的功能会被打包工具移除。

// 只引入需要的功能
import { ref, computed } from 'vue'
// 如果不用 reactive、watch 等,它们不会被打包
// Vue2 中所有功能都会被打包
import Vue from 'vue' // 整个 Vue 对象都会被打包

效果: Vue3 核心运行时压缩后只有 13.5kb(Vue2 约 23kb)

5. 块级作用域追踪(Block Tree)#

Vue3 将模板分割成块,每个块只追踪自己的动态节点。

<div>
<p>静态内容</p>
<div v-if="show">
<p>{{ msg }}</p>
</div>
</div>

编译后会创建一个 Block,只追踪 v-if{{ msg }} 这两个动态部分。

三、响应式系统原理#

1. Proxy vs Object.defineProperty#

Vue3 使用 Proxy 替代了 Vue2 的 Object.defineProperty。

Vue2(Object.defineProperty)的局限:

// Vue2 无法检测到这些变化
const obj = { a: 1 }
obj.b = 2 // 新增属性 - 无法检测
delete obj.a // 删除属性 - 无法检测
const arr = [1, 2, 3]
arr[0] = 4 // 索引赋值 - 需要特殊处理
arr.length = 0 // 修改长度 - 需要特殊处理

Vue3(Proxy)的优势:

// Vue3 可以检测到所有变化
const state = reactive({ a: 1 })
state.b = 2 // ✅ 可以检测
delete state.a // ✅ 可以检测
const arr = reactive([1, 2, 3])
arr[0] = 4 // ✅ 可以检测
arr.length = 0 // ✅ 可以检测

2. 响应式实现原理(简化版)#

// 简化版 reactive 实现
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
// 依赖收集
track(target, key)
const result = Reflect.get(target, key, receiver)
// 如果是对象,递归代理
return isObject(result) ? reactive(result) : result
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
// 触发更新
if (oldValue !== value) {
trigger(target, key)
}
return result
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
// 触发更新
trigger(target, key)
return result
}
})
}
// 依赖收集
let activeEffect = null
const targetMap = new WeakMap()
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}
// 触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => effect())
}
}

3. ref 的实现原理#

// 简化版 ref 实现
function ref(value) {
return {
_isRef: true,
_value: value,
get value() {
track(this, 'value')
return this._value
},
set value(newValue) {
if (newValue !== this._value) {
this._value = newValue
trigger(this, 'value')
}
}
}
}

四、Vue3 新特性#

1. Fragment(片段)#

Vue3 支持组件有多个根节点,不再需要包裹元素。

<!-- Vue2 - 必须有一个根元素 -->
<template>
<div>
<h1>Title</h1>
<p>Content</p>
</div>
</template>
<!-- Vue3 - 可以有多个根节点 -->
<template>
<h1>Title</h1>
<p>Content</p>
</template>

2. Teleport(传送门)#

将组件内容渲染到 DOM 的其他位置。

<template>
<button @click="showModal = true">打开弹窗</button>
<!-- 将弹窗传送到 body 下 -->
<Teleport to="body">
<div v-if="showModal" class="modal">
<h2>这是一个弹窗</h2>
<button @click="showModal = false">关闭</button>
</div>
</Teleport>
</template>
<style>
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
</style>

3. Suspense(悬念)#

处理异步组件的加载状态。

<template>
<Suspense>
<!-- 异步组件 -->
<template #default>
<AsyncComponent />
</template>
<!-- 加载中显示的内容 -->
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
</script>

异步 setup:

AsyncComponent.vue
<script setup>
const data = await fetch('/api/data').then(r => r.json())
</script>
<template>
<div>{{ data }}</div>
</template>

4. 自定义渲染器 API#

Vue3 提供了自定义渲染器 API,可以渲染到非 DOM 平台。

import { createRenderer } from '@vue/runtime-core'
const { render } = createRenderer({
createElement(type) {
// 创建元素
},
setElementText(node, text) {
// 设置文本
},
patchProp(el, key, prevValue, nextValue) {
// 更新属性
},
insert(el, parent, anchor) {
// 插入元素
},
// ... 其他渲染方法
})

五、性能对比#

Vue3 vs Vue2 性能提升#

指标Vue2Vue3提升
初始渲染基准55% 更快⬆️ 55%
更新速度基准133% 更快⬆️ 133%
内存占用基准54% 更少⬇️ 54%
打包体积23kb13.5kb⬇️ 41%

性能优化原因#

  1. 虚拟 DOM 重写:更高效的 diff 算法
  2. 编译时优化:静态提升、补丁标记、事件缓存
  3. Proxy 响应式:更快的响应式性能
  4. Tree Shaking:按需引入,减小体积
  5. 更好的 TypeScript 支持:类型推导减少运行时开销

六、最佳实践#

1. 响应式数据选择#

// ✅ 基本类型使用 ref
const count = ref(0)
const message = ref('hello')
// ✅ 对象使用 reactive
const state = reactive({
user: { name: 'John', age: 30 },
settings: { theme: 'dark' }
})
// ❌ 不推荐:对象使用 ref(需要 .value 访问)
const state = ref({
user: { name: 'John' }
})

2. 避免失去响应式#

// ❌ 解构会失去响应式
const state = reactive({ count: 0 })
let { count } = state // 失去响应式
// ✅ 使用 toRefs
import { toRefs } from 'vue'
const { count } = toRefs(state) // 保持响应式
// ✅ 或者使用 computed
const count = computed(() => state.count)

3. 性能优化技巧#

<script setup>
import { ref, computed, shallowRef } from 'vue'
// 1. 大型不可变数据使用 shallowRef
const bigData = shallowRef({
/* 大量数据 */
})
// 2. 使用 computed 缓存计算结果
const expensiveValue = computed(() => {
// 复杂计算
return /* result */
})
// 3. v-once 渲染静态内容
</script>
<template>
<!-- 只渲染一次 -->
<div v-once>
<h1>{{ staticTitle }}</h1>
</div>
<!-- 使用 v-memo 缓存 -->
<div v-memo="[count]">
<p>这个内容只在 count 变化时更新</p>
</div>
</template>

4. 组合式函数命名规范#

// ✅ 使用 use 前缀
export function useCounter() { }
export function useFetch() { }
export function useLocalStorage() { }
// ❌ 不推荐
export function counter() { }
export function getData() { }

七、迁移建议#

从 Vue2 迁移到 Vue3#

  1. 使用官方迁移构建@vue/compat 提供兼容模式
  2. 渐进式升级:可以在 Vue3 中使用 Options API
  3. 工具支持:使用 Vue DevTools、Volar 等工具
  4. 逐步采用 Composition API:先在新组件中使用

常见迁移问题#

// Vue2
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
}
// Vue3 - 可以继续使用 Options API
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
}
// Vue3 - 推荐使用 Composition API
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
}
// Vue3 - script setup 语法糖(最推荐)
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
</script>

总结#

Vue3 带来了显著的性能提升和更好的开发体验:

  • 性能更好:编译优化、响应式优化、Tree Shaking
  • 类型支持:完整的 TypeScript 支持
  • 代码组织:Composition API 提供更好的逻辑复用
  • 新特性:Fragment、Teleport、Suspense 等实用功能
  • 向后兼容:支持 Options API,迁移成本低

无论是新项目还是老项目升级,Vue3 都是值得考虑的选择。

Vue3 核心知识点详解
https://fuwari.vercel.app/posts/vue3/
作者
Lorem Ipsum
发布于
2025-10-28
许可协议
CC BY-NC-SA 4.0