Vue 3 是 Vue.js 框架的最新主要版本,于 2020 年 9 月正式发布。它在保留 Vue 2 核心开发体验的同时,引入了 Composition API、响应式重构、TypeScript 全面支持、性能大幅提升等革命性特性。本文将系统全面地梳理 Vue 3 的核心知识点,从基础语法到高级特性,帮助初学者快速上手,也为有经验的开发者提供深入的技术参考。
一、Vue 3 概述
1.1 Vue 3 的新特性
核心改进:
Composition API:更好的逻辑复用和代码组织
响应式系统重构:基于 Proxy,性能更好,支持更多数据类型
TypeScript 支持:源码使用 TypeScript 重写,类型推断更完善
性能提升:打包体积减少约 40%,初次渲染快 55%,更新快 133%
Fragment:组件支持多个根节点
Teleport:将组件内容渲染到指定 DOM 位置
Suspense:处理异步组件的加载状态
自定义渲染器 API:可创建自定义渲染器
1.2 环境搭建
bash
# 使用 Vite 创建 Vue 3 项目(推荐)
npm create vue@latest my-vue3-app
cd my-vue3-app
npm install
npm run dev
# 使用 Vue CLI 创建(Webpack)
npm install -g @vue/cli
vue create my-vue3-app
cd my-vue3-app
npm run serve
javascript
// main.js - Vue 3 应用入口
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 创建应用实例
const app = createApp(App)
// 使用插件
app.use(router)
app.use(store)
// 全局配置
app.config.globalProperties.$http = axios
// 全局组件注册
app.component('MyComponent', MyComponent)
// 挂载应用
app.mount('#app')
二、Composition API
2.1 setup 函数
vue
<template>
<div>
<p>{
{ count }}</p>
<p>双倍: {
{ doubleCount }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script>
import { ref, computed } from 'vue'
export default {
name: 'CompositionExample',
// setup 是 Composition API 的入口
// 在组件创建之前执行,在 beforeCreate 和 created 之前
setup(props, context) {
// props: 组件接收的属性(响应式)
// context: 上下文对象,包含 attrs, slots, emit
// 响应式数据
const count = ref(0)
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
const increment = () => {
count.value++
}
// 返回的数据和方法会被模板使用
return {
count,
doubleCount,
increment
}
}
}
</script>
2.2 ref 与 reactive
vue
<script setup>
import { ref, reactive, isRef, isReactive, unref } from 'vue'
// ref - 用于基本类型和对象(通过 .value 访问)
const count = ref(0)
const message = ref('Hello')
const user = ref({ name: '张三', age: 25 })
// 修改值
count.value++
message.value = 'World'
user.value.name = '李四'
// reactive - 只能用于对象类型,直接访问属性
const state = reactive({
count: 0,
user: {
name: '张三',
age: 25
},
list: [1, 2, 3]
})
// 直接修改
state.count++
state.user.name = '李四'
state.list.push(4)
// ref 和 reactive 的区别
const refObj = ref({ name: '张三' })
const reactiveObj = reactive({ name: '张三' })
// ref 需要通过 .value 访问和修改
refObj.value.name = '李四'
// reactive 直接访问
reactiveObj.name = '李四'
// 类型判断
console.log(isRef(refObj)) // true
console.log(isReactive(reactiveObj)) // true
// 自动解包
const countRef = ref(0)
// 在模板中不需要 .value
// 在 reactive 对象中会自动解包
const state2 = reactive({
count: countRef // 不需要 .value
})
console.log(state2.count) // 0
// unref - 如果参数是 ref 则返回 .value,否则返回本身
const value1 = unref(ref(10)) // 10
const value2 = unref(20) // 20
</script>
2.3 计算属性与侦听器
vue
<script setup>
import { ref, reactive, computed, watch, watchEffect } from 'vue'
// 计算属性
const firstName = ref('张')
const lastName = ref('三')
// 只读计算属性
const fullName = computed(() => firstName.value + lastName.value)
// 可写计算属性
const fullNameWritable = computed({
get: () => firstName.value + lastName.value,
set: (newValue) => {
firstName.value = newValue[0]
lastName.value = newValue[1]
}
})
// 侦听器 - watch
const count = ref(0)
const state = reactive({ name: '张三', age: 25 })
// 侦听单个 ref
watch(count, (newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})
// 侦听多个数据源
watch([count, () => state.age], ([newCount, newAge], [oldCount, oldAge]) => {
console.log('count 或 age 变化了')
})
// 侦听对象属性
watch(() => state.name, (newVal, oldVal) => {
console.log(`name 从 ${oldVal} 变为 ${newVal}`)
})
// 深度侦听
watch(() => state, (newVal, oldVal) => {
console.log('state 变化了', newVal)
}, { deep: true })
// 立即执行
watch(count, (newVal) => {
console.log('立即执行', newVal)
}, { immediate: true })
// watchEffect - 自动收集依赖,立即执行
watchEffect(() => {
// 自动追踪 count 和 state.name
console.log(`count: ${count.value}, name: ${state.name}`)
})
// 停止侦听
const stopWatch = watch(count, (newVal) => {
console.log(newVal)
})
// 停止侦听
stopWatch()
// watch 清理副作用
watchEffect((onCleanup) => {
const timer = setInterval(() => {
console.log('执行')
}, 1000)
// 清理副作用
onCleanup(() => {
clearInterval(timer)
})
})
</script>
2.4 生命周期钩子
vue
<script setup>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
onRenderTracked,
onRenderTriggered,
onActivated,
onDeactivated
} from 'vue'
// Composition API 生命周期钩子
// 对应 Options API:beforeCreate -> setup()
// 对应 Options API:created -> setup()
// 组件挂载前
onBeforeMount(() => {
console.log('组件挂载前')
})
// 组件挂载后
onMounted(() => {
console.log('组件挂载后')
// DOM 已渲染,可以访问 DOM 元素
})
// 组件更新前
onBeforeUpdate(() => {
console.log('组件更新前')
})
// 组件更新后
onUpdated(() => {
console.log('组件更新后')
})
// 组件卸载前
onBeforeUnmount(() => {
console.log('组件卸载前')
})
// 组件卸载后
onUnmounted(() => {
console.log('组件卸载后')
})
// 错误捕获
onErrorCaptured((err, instance, info) => {
console.log('捕获错误:', err)
// 返回 false 可以阻止错误继续传播
return false
})
// 渲染追踪(开发环境)
onRenderTracked((event) => {
console.log('渲染追踪:', event)
})
// 渲染触发(开发环境)
onRenderTriggered((event) => {
console.log('渲染触发:', event)
})
// keep-alive 组件激活时
onActivated(() => {
console.log('组件激活')
})
// keep-alive 组件停用时
onDeactivated(() => {
console.log('组件停用')
})
</script>
三、响应式原理
3.1 Proxy 响应式系统
javascript
// Vue 3 响应式原理示例
function reactive(target) {
if (typeof target !== 'object' || target === null) {
return target
}
return new Proxy(target, {
get(target, key, receiver) {
// 依赖收集
track(target, key)
const result = Reflect.get(target, key, receiver)
// 深度响应式
return reactive(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 hadKey = Object.prototype.hasOwnProperty.call(target, key)
const result = Reflect.deleteProperty(target, key)
if (hadKey) {
trigger(target, key)
}
return result
}
})
}
// 依赖收集
const targetMap = new WeakMap()
function track(target, key) {
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
// 添加当前活动的 effect
if (activeEffect) {
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())
}
}
// effect 函数
let activeEffect = null
function effect(fn) {
activeEffect = fn
fn()
activeEffect = null
}
3.2 ref 原理
javascript
// ref 的实现原理
function ref(value) {
return {
_value: value,
get value() {
// 依赖收集
track(this, 'value')
return this._value
},
set value(newValue) {
if (this._value !== newValue) {
this._value = newValue
// 触发更新
trigger(this, 'value')
}
}
}
}