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) // 1reactive - 对象响应式
import { reactive } from 'vue'
const state = reactive({ count: 0, message: 'Hello'})
state.count++ // 直接访问,不需要 .valuecomputed - 计算属性
import { ref, computed } from 'vue'
const count = ref(1)const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2count.value++console.log(plusOne.value) // 3watch 和 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 提取和复用逻辑:
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 = nullconst 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:
<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 性能提升
| 指标 | Vue2 | Vue3 | 提升 |
|---|---|---|---|
| 初始渲染 | 基准 | 55% 更快 | ⬆️ 55% |
| 更新速度 | 基准 | 133% 更快 | ⬆️ 133% |
| 内存占用 | 基准 | 54% 更少 | ⬇️ 54% |
| 打包体积 | 23kb | 13.5kb | ⬇️ 41% |
性能优化原因
- 虚拟 DOM 重写:更高效的 diff 算法
- 编译时优化:静态提升、补丁标记、事件缓存
- Proxy 响应式:更快的响应式性能
- Tree Shaking:按需引入,减小体积
- 更好的 TypeScript 支持:类型推导减少运行时开销
六、最佳实践
1. 响应式数据选择
// ✅ 基本类型使用 refconst count = ref(0)const message = ref('hello')
// ✅ 对象使用 reactiveconst 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 // 失去响应式
// ✅ 使用 toRefsimport { toRefs } from 'vue'const { count } = toRefs(state) // 保持响应式
// ✅ 或者使用 computedconst count = computed(() => state.count)3. 性能优化技巧
<script setup>import { ref, computed, shallowRef } from 'vue'
// 1. 大型不可变数据使用 shallowRefconst 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
- 使用官方迁移构建:
@vue/compat提供兼容模式 - 渐进式升级:可以在 Vue3 中使用 Options API
- 工具支持:使用 Vue DevTools、Volar 等工具
- 逐步采用 Composition API:先在新组件中使用
常见迁移问题
// Vue2export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } }}
// Vue3 - 可以继续使用 Options APIexport default { data() { return { count: 0 } }, methods: { increment() { this.count++ } }}
// Vue3 - 推荐使用 Composition APIimport { 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/