Vue3 开发指南
🌟 简介
Vue3 是 Vue.js 的最新主要版本,带来了革命性的组合式 API、显著的性能提升和丰富的新特性。本文将全面介绍 Vue3 的核心概念、实战技巧和最佳实践,帮助你快速掌握这一现代前端框架。
🚀 组合式 API
1.1 基础用法
组合式 API 是 Vue3 的核心特性,它允许我们按照逻辑关注点组织代码,而不是按照选项类型。下面是一个基本的使用示例:
javascript
// 导入必要的组合式 API
import { ref, computed, onMounted } from 'vue'
export default {
// setup 函数是组合式 API 的入口点
// 在组件实例创建之前执行,返回的内容会暴露给模板
setup() {
// 使用 ref 创建响应式数据(适用于基本类型)
// ref 返回一个响应式的引用对象,需要通过 .value 访问和修改
const count = ref(0)
// 使用 computed 创建计算属性
// 计算属性会缓存结果,只有依赖项变化时才会重新计算
const doubleCount = computed(() => count.value * 2)
// 定义方法
function increment() {
// 修改 ref 包装的值需要使用 .value
count.value++
}
// 使用生命周期钩子
// 组合式 API 中的生命周期钩子以 on 开头
onMounted(() => {
console.log('Component mounted')
})
// 返回需要暴露给模板的内容
return {
count,
doubleCount,
increment
}
}
}1.2 组合式函数
组合式函数是 Vue3 中代码复用的最佳方式,它允许我们将相关的响应式状态和逻辑封装成可复用的函数。下面是一个计数器组合式函数的示例:
javascript
// useCounter.js
import { ref, computed } from 'vue'
/**
* 计数器组合式函数
* @param {number} initialValue - 初始值,默认为 0
* @returns {Object} 包含计数状态和操作方法的对象
*/
export function useCounter(initialValue = 0) {
// 创建响应式计数状态
const count = ref(initialValue)
// 创建计算属性,实时计算双倍值
const doubleCount = computed(() => count.value * 2)
// 增加计数的方法
function increment() {
count.value++
}
// 减少计数的方法
function decrement() {
count.value--
}
// 返回需要暴露的状态和方法
return {
count,
doubleCount,
increment,
decrement
}
}
// 使用组合式函数
import { useCounter } from './useCounter'
export default {
setup() {
// 调用组合式函数,传入初始值 10
// 解构获取需要的状态和方法
const { count, doubleCount, increment } = useCounter(10)
return {
count,
doubleCount,
increment
}
}
}🔄 响应式系统
2.1 ref 和 reactive
Vue3 提供了两种创建响应式数据的方式:ref 和 reactive。它们各有适用场景,下面是详细的使用示例:
javascript
import { ref, reactive } from 'vue'
export default {
setup() {
// ref 适用于基本类型(数字、字符串、布尔值等)
// 也可以用于对象,但需要通过 .value 访问
const count = ref(0)
// reactive 适用于对象和数组
// 直接返回一个响应式的代理对象,不需要 .value
const state = reactive({
user: {
name: 'John',
age: 30
},
todos: []
})
// 修改响应式数据
function updateUser() {
// 修改 ref 包装的值需要使用 .value
// count.value = 1
// 修改 reactive 包装的对象直接赋值即可
state.user.name = 'Jane'
state.user.age = 25
}
// 返回需要暴露给模板的内容
return {
count,
state,
updateUser
}
}
}2.2 计算属性和侦听器
计算属性和侦听器是 Vue 中处理响应式数据的重要工具,它们分别用于不同的场景:
javascript
import { ref, reactive, computed, watch } from 'vue'
export default {
setup() {
// 创建响应式数据
const count = ref(0)
const state = reactive({
user: {
name: 'John',
age: 30
}
})
// 计算属性
// 用于根据其他响应式数据派生新值
// 会缓存结果,只有依赖项变化时才重新计算
const doubleCount = computed(() => count.value * 2)
// 基本侦听器
// 用于监听响应式数据的变化并执行副作用
watch(count, (newValue, oldValue) => {
console.log('count changed:', oldValue, '->', newValue)
// 可以在这里执行异步操作、API 调用等
})
// 深度侦听
// 用于监听嵌套对象的变化
// 需要设置 deep: true 选项
watch(
() => state.user, // 使用函数返回值作为侦听源
(newValue, oldValue) => {
console.log('user changed:', oldValue, '->', newValue)
},
{ deep: true }
)
// 立即执行的侦听器
// 用于在组件创建时立即执行一次
watch(
count,
(newValue) => {
console.log('Current count:', newValue)
},
{ immediate: true }
)
return {
count,
doubleCount,
state
}
}
}⏰ 生命周期钩子
Vue3 提供了与 Vue2 对应的生命周期钩子,但在组合式 API 中,它们以函数形式存在,并且需要从 Vue 中导入。以下是完整的生命周期钩子使用示例:
javascript
import {
onMounted,
onUpdated,
onUnmounted,
onBeforeMount,
onBeforeUpdate,
onBeforeUnmount,
onErrorCaptured,
onRenderTracked,
onRenderTriggered
} from 'vue'
export default {
setup() {
// 挂载前
onBeforeMount(() => {
console.log('Before mount')
})
// 挂载后
onMounted(() => {
console.log('Mounted')
})
// 更新前
onBeforeUpdate(() => {
console.log('Before update')
})
// 更新后
onUpdated(() => {
console.log('Updated')
})
// 卸载前
onBeforeUnmount(() => {
console.log('Before unmount')
})
// 卸载后
onUnmounted(() => {
console.log('Unmounted')
})
// 错误捕获
onErrorCaptured((error) => {
console.error('Error captured:', error)
return true // 阻止错误继续传播
})
// 渲染跟踪
onRenderTracked((event) => {
console.log('Render tracked:', event)
})
// 渲染触发
onRenderTriggered((event) => {
console.log('Render triggered:', event)
})
}
}📝 模板语法
4.1 指令
vue
<template>
<!-- v-model -->
<input v-model="message" />
<!-- v-if -->
<div v-if="isVisible">Visible content</div>
<!-- v-for -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
<!-- v-on -->
<button @click="handleClick">Click me</button>
<!-- v-bind -->
<img :src="imageUrl" :alt="imageAlt" />
</template>
<script setup>
import { ref } from 'vue'
const message = ref('')
const isVisible = ref(true)
const items = ref([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
])
const imageUrl = ref('/path/to/image.jpg')
const imageAlt = ref('Image description')
function handleClick() {
console.log('Button clicked')
}
</script>4.2 插槽
vue
<!-- 子组件 -->
<template>
<div class="card">
<header>
<slot name="header">Default header</slot>
</header>
<main>
<slot>Default content</slot>
</main>
<footer>
<slot name="footer">Default footer</slot>
</footer>
</div>
</template>
<!-- 父组件 -->
<template>
<Card>
<template #header>
<h1>Custom header</h1>
</template>
<p>Custom content</p>
<template #footer>
<p>Custom footer</p>
</template>
</Card>
</template>🗃️ 状态管理
5.1 Pinia
javascript
// store/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
async fetchCount() {
const response = await fetch('/api/count')
this.count = await response.json()
}
}
})
// 使用 store
import { useCounterStore } from '@/store/counter'
export default {
setup() {
const counter = useCounterStore()
return {
count: counter.count,
doubleCount: counter.doubleCount,
increment: counter.increment
}
}
}5.2 组合式 API 状态管理
javascript
// composables/useStore.js
import { reactive } from 'vue'
export function useStore() {
const state = reactive({
count: 0,
todos: []
})
function increment() {
state.count++
}
function addTodo(todo) {
state.todos.push(todo)
}
return {
state,
increment,
addTodo
}
}⚡ 性能优化
6.1 组件优化
javascript
// 异步组件
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
)
// 组件缓存
<template>
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
</template>
// 虚拟列表
<template>
<virtual-scroller
:items="items"
:item-height="50"
>
<template v-slot="{ item }">
<div class="item">{{ item.text }}</div>
</template>
</virtual-scroller>
</template>6.2 渲染优化
javascript
// 使用 shallowRef
import { shallowRef } from 'vue'
const state = shallowRef({
user: {
name: 'John',
address: {
city: 'New York'
}
}
})
// 使用 markRaw
import { markRaw } from 'vue'
const rawObject = markRaw({
complex: true,
nested: {
data: 'value'
}
})🎯 最佳实践
7.1 项目结构
合理的项目结构是构建可维护应用的基础,以下是一个推荐的 Vue3 项目结构及其用途说明:
bash
src/
components/ # 组件目录
Base/ # 基础组件(按钮、输入框等)
Layout/ # 布局组件(头部、侧边栏等)
Business/ # 业务组件(特定功能模块)
composables/ # 组合式函数目录
stores/ # 状态管理目录(Pinia store)
views/ # 页面组件目录
router/ # 路由配置目录
utils/ # 工具函数目录
assets/ # 静态资源目录(图片、字体等)
styles/ # 全局样式目录
services/ # API 服务目录
types/ # TypeScript 类型定义目录实际应用场景:
- 大型项目:可以按照功能模块进一步划分目录,如
components/user/、components/product/等 - 团队协作:明确的目录结构有助于团队成员快速定位和理解代码
- 代码复用:将通用逻辑提取到
composables/和utils/目录,提高代码复用率
7.2 代码组织
在 Vue3 中,组合式 API 为我们提供了更灵活的代码组织方式。以下是一个用户认证组合式函数的实际应用示例:
javascript
// composables/useUser.js
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
/**
* 用户认证组合式函数
* 封装用户登录、登出和状态管理逻辑
*/
export function useUser() {
// 用户信息状态
const user = ref(null)
// 登录状态计算属性
const isLoggedIn = computed(() => !!user.value)
// 加载状态
const isLoading = ref(false)
// 错误信息
const error = ref(null)
// 路由实例
const router = useRouter()
/**
* 用户登录
* @param {Object} credentials - 登录凭证
* @param {string} credentials.username - 用户名
* @param {string} credentials.password - 密码
*/
async function login(credentials) {
isLoading.value = true
error.value = null
try {
// 模拟 API 调用
// const response = await api.login(credentials)
// user.value = response.data
// 模拟登录成功
user.value = {
id: 1,
username: credentials.username,
name: '测试用户'
}
// 登录成功后跳转到首页
router.push('/')
} catch (err) {
error.value = '登录失败,请检查用户名和密码'
console.error('Login error:', err)
} finally {
isLoading.value = false
}
}
/**
* 用户登出
*/
async function logout() {
isLoading.value = true
try {
// 模拟 API 调用
// await api.logout()
// 清除用户信息
user.value = null
// 跳转到登录页
router.push('/login')
} catch (err) {
console.error('Logout error:', err)
} finally {
isLoading.value = false
}
}
/**
* 初始化用户信息
* 用于页面刷新后恢复用户状态
*/
async function initUser() {
isLoading.value = true
try {
// 模拟从本地存储或 API 获取用户信息
// const savedUser = localStorage.getItem('user')
// if (savedUser) {
// user.value = JSON.parse(savedUser)
// }
} catch (err) {
console.error('Init user error:', err)
} finally {
isLoading.value = false
}
}
// 返回状态和方法
return {
user,
isLoggedIn,
isLoading,
error,
login,
logout,
initUser
}
}
// 使用组合式函数
import { useUser } from '@/composables/useUser'
import { onMounted } from 'vue'
export default {
setup() {
const { user, isLoggedIn, isLoading, error, login, logout, initUser } = useUser()
// 组件创建时初始化用户信息
onMounted(() => {
initUser()
})
return {
user,
isLoggedIn,
isLoading,
error,
login,
logout
}
}
}实际应用场景:
- 跨组件复用:在登录页、用户信息页等多个组件中复用认证逻辑
- 逻辑关注点分离:将用户认证相关的所有逻辑集中在一个地方,便于维护
- 类型安全:结合 TypeScript 可以为组合式函数添加类型定义,提高代码可靠性
- 测试友好:组合式函数可以单独测试,不需要渲染整个组件
🛠️ 工具链
8.1 开发工具
json
{
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"vue": "^3.2.0",
"pinia": "^2.0.0",
"vue-router": "^4.0.0",
"typescript": "^4.5.0",
"eslint": "^8.0.0",
"prettier": "^2.0.0"
}
}8.2 测试工具
json
{
"devDependencies": {
"@vue/test-utils": "^2.0.0",
"vitest": "^0.34.0",
"@testing-library/vue": "^7.0.0",
"cypress": "^12.0.0"
}
}📋 总结
Vue3 作为现代前端框架的佼佼者,通过以下核心特性为我们带来了革命性的开发体验:
- 组合式 API:提供了更灵活、更可复用的代码组织方式,彻底解决了 Options API 中逻辑分散的问题
- 响应式系统升级:基于 Proxy 的响应式系统,提供了更好的性能和更直观的 API
- 性能优化:编译时优化、虚拟 DOM 改进、Tree-shaking 支持等,显著提升了应用性能
- TypeScript 支持:更好的类型推断和类型检查,提高了代码的可靠性
- 生态系统完善:Pinia 状态管理、Vite 构建工具等,形成了完整的开发生态
通过本文介绍的最佳实践和实际应用场景,你可以充分发挥 Vue3 的优势,构建出高效、可维护的现代化前端应用。
📚 参考资源
官方文档
- Vue3 官方文档 - 最权威、最全面的 Vue3 参考资料
- 组合式 API 文档 - 深入了解组合式 API 的设计理念和使用方法
- Pinia 官方文档 - Vue3 官方推荐的状态管理库
- Vue Router 4 文档 - Vue3 配套的路由库
学习资源
- Vue Mastery - 高质量的 Vue 视频教程
- Vue School - 互动式 Vue 学习平台
- Vue 官方示例 - 丰富的 Vue3 示例代码
工具与插件
- Vite - 极速的前端构建工具,Vue3 官方推荐
- ESLint Vue 插件 - Vue 代码质量检查工具
- Vue DevTools - 强大的 Vue 开发调试工具
社区资源
- Vue 论坛 - Vue 官方社区论坛
- Vue GitHub - Vue 源代码仓库
- Vue 生态系统 - Vue 生态系统概览