Skip to Content
🚀 欢迎来到前端学习指南!这是一份从零基础到专家的完整学习路径
⚛️ 第二部分:框架篇08. Vue3深入和组合式API

08. Vue3深入和组合式API

📋 目录

Vue3架构革新

Vue3带来了全新的架构设计,包括Composition API、更好的TypeScript支持、Tree-shaking友好的设计等重大改进。

Vue3 vs Vue2 核心差异

API风格对比

特性Vue2 Options APIVue3 Composition API优势
代码组织按选项类型分组按逻辑功能分组更好的代码复用和维护
TypeScript支持有限原生支持更好的类型推断
Tree-shaking不支持完全支持更小的打包体积
逻辑复用MixinsComposables避免命名冲突

代码风格对比

// Vue2 Options API export default { data() { return { count: 0, user: null } }, computed: { doubleCount() { return this.count * 2 } }, methods: { increment() { this.count++ }, async fetchUser() { this.user = await api.getUser() } }, mounted() { this.fetchUser() } } // Vue3 Composition API import { ref, computed, onMounted } from 'vue' export default { setup() { const count = ref(0) const user = ref(null) const doubleCount = computed(() => count.value * 2) const increment = () => count.value++ const fetchUser = async () => user.value = await api.getUser() onMounted(fetchUser) return { count, user, doubleCount, increment, fetchUser } } } // Vue3 <script setup> 语法糖(推荐) import { ref, computed, onMounted } from 'vue' const count = ref(0) const user = ref(null) const doubleCount = computed(() => count.value * 2) const increment = () => count.value++ const fetchUser = async () => user.value = await api.getUser() onMounted(fetchUser)

Vue3新特性概览

核心新特性

特性说明优势使用场景
Fragment支持多个根节点更灵活的模板结构布局组件
Teleport将内容传送到DOM其他位置解决样式层级问题模态框、提示框
Suspense异步组件加载状态管理更好的加载体验代码分割、异步数据
<script setup>更简洁的组合式API语法减少样板代码所有组件

新特性示例

<!-- Fragment:多个根节点 --> <template> <header>头部</header> <main>内容</main> <footer>底部</footer> </template> <!-- Teleport:传送门 --> <template> <div> <button @click="showModal = true">打开模态框</button> <Teleport to="body"> <div v-if="showModal" class="modal"> <p>模态框内容</p> <button @click="showModal = false">关闭</button> </div> </Teleport> </div> </template> <!-- Suspense:异步组件 --> <template> <Suspense> <template #default> <AsyncUserProfile :user-id="userId" /> </template> <template #fallback> <div class="loading">加载用户信息中...</div> </template> </Suspense> </template> <!-- <script setup>:语法糖 --> <script setup> import { ref, computed, watch, onMounted } from 'vue' // 响应式数据 const count = ref(0) const showModal = ref(false) // 计算属性 const doubleCount = computed(() => count.value * 2) // 侦听器 watch(count, (newVal) => { console.log(`count: ${newVal}`) }) // 生命周期 onMounted(() => { console.log('组件已挂载') }) // 方法(自动暴露给模板) const increment = () => count.value++ </script>

响应式系统原理

Proxy vs Object.defineProperty

⚠️

Vue3使用Proxy替代了Vue2的Object.defineProperty,解决了数组和对象属性动态添加的响应式问题。

技术对比

特性Vue2 Object.definePropertyVue3 Proxy优势
数组监听需要特殊处理原生支持更完整的数组响应式
属性添加需要Vue.set自动监听动态属性响应式
属性删除需要Vue.delete自动监听完整的删除监听
性能初始化时遍历所有属性懒代理更好的初始化性能
浏览器支持IE8+IE11+现代浏览器特性

核心实现原理

// Vue2 响应式(简化) function defineReactive(obj, key, val) { Object.defineProperty(obj, key, { get() { if (Dep.target) dep.depend() // 依赖收集 return val }, set(newVal) { if (newVal === val) return val = newVal dep.notify() // 通知更新 } }) } // Vue3 响应式(简化) function reactive(target) { return new Proxy(target, { get(target, key, receiver) { const result = Reflect.get(target, key, receiver) track(target, key) // 依赖收集 // 深度响应式 if (typeof result === 'object' && result !== null) { return reactive(result) } return 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 } }) }

响应式系统优势

  • 更完整的拦截:Proxy可以拦截13种操作
  • 更好的性能:懒代理,按需创建响应式对象
  • 更简洁的API:不需要特殊的set/delete方法
  • 更好的数组支持:原生支持数组索引和length变化

响应式API详解

响应式API对比

API用途适用场景特点
ref基本类型响应式数字、字符串、布尔值需要.value访问
reactive对象响应式对象、数组深度响应式,直接访问属性
computed计算属性基于其他响应式数据的派生值缓存结果,依赖变化时重新计算
watch侦听器监听特定数据变化可访问新旧值
watchEffect副作用侦听器自动追踪依赖的副作用立即执行,自动收集依赖

基础用法示例

// 1. ref - 基本类型响应式 import { ref, isRef, unref } from 'vue' const count = ref(0) const message = ref('hello') console.log(count.value) // 0 count.value++ // 触发响应式更新 // 工具函数 console.log(isRef(count)) // true console.log(unref(count)) // 1,等同于 count.value // 2. reactive - 对象响应式 import { reactive, isReactive, toRaw } from 'vue' const state = reactive({ count: 0, nested: { message: 'hello' } }) // 深度响应式 state.nested.message = 'world' // 触发更新 console.log(isReactive(state.nested)) // true // 3. computed - 计算属性 import { computed } from 'vue' const count = ref(1) const plusOne = computed(() => count.value + 1) // 可写计算属性 const writableComputed = computed({ get: () => count.value + 1, set: (val) => count.value = val - 1 }) // 4. watch 和 watchEffect import { watch, watchEffect } from 'vue' // 侦听单个源 watch(() => state.count, (newVal, oldVal) => { console.log(`count: ${oldVal} -> ${newVal}`) }) // 侦听多个源 watch([() => state.count, () => state.name], ([newCount, newName]) => { console.log('多个值发生变化') }) // 立即执行的侦听器 const stop = watchEffect(() => { console.log(`count is ${state.count}`) }) // 停止侦听 stop()

最佳实践

场景推荐API理由
基本类型ref简单直接,类型安全
对象/数组reactive使用更自然,无需.value
派生数据computed自动缓存,性能更好
副作用watchEffect自动依赖收集
精确控制watch可访问新旧值,更灵活

Composition API深入

组合式函数(Composables)

组合式函数是Vue3的核心特性,它允许我们将相关的逻辑组合在一起,提高代码的复用性和可维护性。

常用组合式函数

函数名功能返回值使用场景
useCounter计数器逻辑count, increment, decrement数量控制、分页
useFetch数据获取data, loading, errorAPI调用
useLocalStorage本地存储响应式存储值用户偏好设置
useEventListener事件监听DOM事件处理
useIntersectionObserver交叉观察isIntersecting, entries懒加载、无限滚动

组合式函数示例

// 1. useCounter - 计数器 import { ref, computed, readonly } from 'vue' export function useCounter(initialValue = 0) { const count = ref(initialValue) const increment = () => count.value++ const decrement = () => count.value-- const reset = () => count.value = initialValue const isEven = computed(() => count.value % 2 === 0) return { count: readonly(count), increment, decrement, reset, isEven } } // 2. useFetch - 数据获取 import { ref, watchEffect, readonly } from 'vue' export function useFetch(url) { const data = ref(null) const error = ref(null) const loading = ref(false) const fetchData = async () => { loading.value = true error.value = null try { const response = await fetch(url.value || url) data.value = await response.json() } catch (err) { error.value = err } finally { loading.value = false } } // 响应式URL自动重新获取 if (isRef(url)) { watchEffect(fetchData) } else { fetchData() } return { data: readonly(data), error: readonly(error), loading: readonly(loading), refetch: fetchData } } // 3. useLocalStorage - 本地存储 import { ref, watch } from 'vue' export function useLocalStorage(key, defaultValue) { const storedValue = localStorage.getItem(key) const initialValue = storedValue ? JSON.parse(storedValue) : defaultValue const value = ref(initialValue) // 同步到localStorage watch(value, (newValue) => { localStorage.setItem(key, JSON.stringify(newValue)) }, { deep: true }) return value }

组合式函数使用

// 在组件中使用 export default { setup() { // 计数器功能 const { count, increment, isEven } = useCounter(10) // 数据获取 const { data: users, loading, error } = useFetch('/api/users') // 本地存储 const theme = useLocalStorage('theme', 'light') return { count, increment, isEven, users, loading, error, theme } } }

高级组合模式

高级组合式函数

函数名功能适用场景复杂度
useStateMachine状态机管理复杂状态流转
useFormValidation表单验证表单处理
useInfiniteScroll无限滚动列表加载
useDebounce防抖处理搜索输入

实现示例

// 1. 状态机组合式函数 export function useStateMachine(initialState, transitions) { const state = ref(initialState) const transition = (action) => { const currentTransitions = transitions[state.value] if (currentTransitions?.[action]) { state.value = currentTransitions[action] } } const can = (action) => { const currentTransitions = transitions[state.value] return currentTransitions?.[action] !== undefined } return { state: readonly(state), transition, can } } // 使用状态机 const { state, transition, can } = useStateMachine('idle', { idle: { start: 'loading' }, loading: { success: 'success', error: 'error' }, success: { reset: 'idle' }, error: { retry: 'loading', reset: 'idle' } }) // 2. 表单验证组合式函数 export function useFormValidation(rules) { const errors = ref({}) const isValid = computed(() => Object.keys(errors.value).length === 0) const validate = (field, value) => { const fieldRules = rules[field] if (!fieldRules) return true for (const rule of fieldRules) { if (!rule.validator(value)) { errors.value[field] = rule.message return false } } delete errors.value[field] return true } return { errors: readonly(errors), isValid, validate, validateAll: (values) => { return Object.entries(values).every(([field, value]) => validate(field, value)) } } } // 3. 防抖组合式函数 export function useDebounce(fn, delay = 300) { let timeoutId = null const debouncedFn = (...args) => { clearTimeout(timeoutId) timeoutId = setTimeout(() => fn(...args), delay) } const cancel = () => clearTimeout(timeoutId) return { debouncedFn, cancel } }

组合式函数最佳实践

  • 单一职责:每个函数只负责一个功能域
  • 返回只读状态:使用readonly()防止外部修改
  • 提供清理机制:在onUnmounted中清理副作用
  • 支持响应式参数:参数可以是ref或reactive
  • 错误处理:提供完善的错误处理机制

组件通信和状态管理

组件通信方式

通信方式对比

方式适用场景数据流向复杂度
Props/Emits父子组件双向
Provide/Inject跨层级组件单向
模板引用直接访问子组件双向
Pinia全局状态双向
Event Bus任意组件双向

基础通信示例

<!-- 1. Props/Emits 父子通信 --> <!-- 父组件 --> <template> <ChildComponent :message="parentMessage" :count="count" @update:count="count = $event" @custom-event="handleCustomEvent" /> </template> <script setup> import { ref } from 'vue' const parentMessage = ref('来自父组件的消息') const count = ref(0) const handleCustomEvent = (data) => { console.log('收到自定义事件:', data) } </script> <!-- 子组件 --> <script setup> const props = defineProps({ message: String, count: { type: Number, default: 0 } }) const emit = defineEmits(['update:count', 'custom-event']) const updateCount = () => emit('update:count', props.count + 1) const emitCustomEvent = () => emit('custom-event', { timestamp: Date.now() }) </script> <!-- 2. Provide/Inject 跨层级通信 --> <!-- 祖先组件 --> <script setup> import { provide, ref, readonly } from 'vue' const theme = ref('dark') const user = ref({ name: 'John', role: 'admin' }) provide('theme', theme) provide('user', readonly(user)) provide('updateTheme', (newTheme) => theme.value = newTheme) </script> <!-- 后代组件 --> <script setup> import { inject } from 'vue' const theme = inject('theme') const user = inject('user') const updateTheme = inject('updateTheme') const config = inject('config', { api: '/api' }) // 默认值 </script> <!-- 3. 模板引用 --> <template> <ChildComponent ref="childRef" /> <button @click="childRef?.someMethod()">调用子组件方法</button> </template> <script setup> import { ref } from 'vue' const childRef = ref() </script>

Pinia状态管理

Pinia vs Vuex

特性PiniaVuex优势
TypeScript支持原生支持需要额外配置更好的类型推断
代码分割自动支持需要手动配置更好的Tree-shaking
DevTools完整支持完整支持调试体验相当
学习曲线更简单相对复杂更容易上手
包大小更小较大更轻量

Store定义方式

// 1. Option Store(类似Vuex) export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, name: 'Eduardo' }), getters: { doubleCount: (state) => state.count * 2 }, actions: { increment() { this.count++ }, async fetchUserData() { const userData = await api.getUser() this.name = userData.name } } }) // 2. Composition Store(推荐) export const useCounterStore = defineStore('counter', () => { // state const count = ref(0) const name = ref('Eduardo') // getters const doubleCount = computed(() => count.value * 2) // actions const increment = () => count.value++ const fetchUserData = async () => { const userData = await api.getUser() name.value = userData.name } return { count, name, doubleCount, increment, fetchUserData } })

组件中使用

// 在组件中使用Pinia import { useCounterStore } from '@/stores/counter' import { storeToRefs } from 'pinia' const store = useCounterStore() // 解构响应式数据(需要storeToRefs) const { count, name, doubleCount } = storeToRefs(store) // 解构actions(不需要storeToRefs) const { increment, fetchUserData } = store // 批量更新 store.$patch({ count: 10, name: 'New Name' })

Vue3性能优化

Vue3在性能方面有显著提升,包括更小的包体积、更快的渲染速度和更好的Tree-shaking支持。

Vue3性能优化策略

性能优化对比

优化类型Vue2Vue3提升效果
包体积34KB13.5KB60%减少
初始渲染基准55%提升更快的首屏
更新性能基准133%提升更快的重渲染
内存使用基准54%减少更少的内存占用

编译时优化

<template> <!-- 静态提升:静态元素提升到渲染函数外部 --> <div class="static-class"> <h1>静态标题</h1> <!-- 只有动态内容会重新渲染 --> <p>{{ message }}</p> <div :class="dynamicClass">{{ count }}</div> </div> </template> <script setup> import { ref, computed } from 'vue' const message = ref('Hello Vue3') const count = ref(0) const dynamicClass = computed(() => count.value > 10 ? 'large' : 'small') </script>

响应式优化技巧

// 1. 使用shallowRef减少深度响应式开销 import { shallowRef, triggerRef, markRaw } from 'vue' const state = shallowRef({ count: 0, nested: { value: 'hello' } }) // 手动触发更新 const updateNested = () => { state.value.nested.value = 'world' triggerRef(state) // 手动触发响应式更新 } // 2. 使用markRaw避免响应式转换 const state = reactive({ foo: markRaw({ // 这个对象不会被转换为响应式 nestedProperty: 'value' }) })

组件优化技巧

<!-- 1. 使用v-memo缓存子树 --> <template> <div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]"> <p>{{ item.name }}</p> <p>{{ item.description }}</p> </div> </template> <script setup> // 2. 异步组件加载 import { defineAsyncComponent } from 'vue' const AsyncComponent = defineAsyncComponent({ loader: () => import('./HeavyComponent.vue'), loadingComponent: LoadingComponent, delay: 200, timeout: 3000 }) </script>

性能优化清单

  • ✅ 使用shallowRefshallowReactive减少深度响应式
  • ✅ 使用markRaw标记不需要响应式的对象
  • ✅ 使用v-memo缓存复杂的列表项
  • ✅ 使用异步组件进行代码分割
  • ✅ 合理使用computed缓存计算结果
  • ✅ 避免在模板中使用复杂表达式

Vue3生态系统

Vue3拥有丰富的生态系统,包括官方工具、第三方库和开发工具。

Vue3生态系统概览

核心工具对比

工具类型Vue3推荐特点适用场景
构建工具Vite快速热更新、原生ESM现代项目
脚手架create-vue官方模板、TypeScript支持新项目
路由Vue Router 4Composition API支持SPA应用
状态管理Pinia轻量、TypeScript友好全局状态
UI库Element Plus, Vuetify 3Vue3原生支持企业应用

快速开始

# 创建Vue3项目 npm create vue@latest my-vue-app cd my-vue-app npm install npm run dev

路由配置

// Vue Router 4 基础配置 import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', name: 'Home', component: () => import('./views/Home.vue') }, { path: '/user/:id', name: 'User', component: () => import('./views/User.vue'), props: true } ] })

主要UI组件库

// Element Plus(推荐) import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' app.use(ElementPlus) // Ant Design Vue import Antd from 'ant-design-vue' import 'ant-design-vue/dist/antd.css' app.use(Antd)

开发工具推荐

工具功能安装方式
Vue DevTools调试Vue组件和状态浏览器扩展
VolarVS Code Vue3支持VS Code扩展
Vitest单元测试框架npm install vitest
Vue Test UtilsVue组件测试工具npm install @vue/test-utils

测试示例

// Vitest + Vue Test Utils import { mount } from '@vue/test-utils' import { describe, it, expect } from 'vitest' describe('MyComponent', () => { it('renders properly', () => { const wrapper = mount(MyComponent, { props: { msg: 'Hello Vitest' } }) expect(wrapper.text()).toContain('Hello Vitest') }) })

Vue3的Composition API和响应式系统为我们提供了更灵活、更强大的开发体验。通过合理使用组合式函数,我们可以构建更加模块化和可复用的代码。


📚 参考学习资料

📖 官方文档

🎓 优质教程

🛠️ 实践项目

🔧 开发工具

📝 深入阅读

💡 学习建议:建议从官方文档开始,通过Vite创建项目实践,然后深入学习Composition API和响应式系统,最后了解Vue3生态系统。

Last updated on