一、Vue 3带来的革命性变化
Vue 3自2020年发布以来,已经成为现代前端开发的首选框架之一。相较于Vue 2,Vue 3在性能、开发体验和可维护性方面都有显著提升。本文将重点介绍Vue 3最核心的三大特性:Composition API、Pinia状态管理和Vue Router路由守卫,通过实战示例带你掌握这些技术的精髓。
Vue 3的主要改进
-
性能提升:重写了虚拟DOM,更新速度提升1.3-2倍
-
Tree-shaking支持:打包体积减少41%
-
Composition API:更好的逻辑组织和代码复用
-
更好的TypeScript支持:代码提示更完善
-
新的响应式系统:基于Proxy实现,性能更优
二、Composition API深度解析
1. 为什么需要Composition API?
Options API在组件逻辑复杂时存在几个问题:
-
相关逻辑分散在不同选项中(data、methods、computed等)
-
代码复用困难(mixins存在命名冲突等问题)
-
类型推断不够友好
Composition API通过将相关逻辑组织在一起,解决了这些问题。
2. 基础用法示例
<script setup>
import { ref, computed, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
// 方法
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log('组件已挂载')
})
</script>
<template>
<button @click="increment">
点击次数: {{ count }}, 双倍: {{ doubleCount }}
</button>
</template>
3. 逻辑复用:自定义组合函数
// useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
function increment() {
count.value++
}
function decrement() {
count.value--
}
function reset() {
count.value = initialValue
}
return {
count,
increment,
decrement,
reset
}
}
// 在组件中使用
<script setup>
import { useCounter } from './useCounter'
const { count, increment } = useCounter(10)
</script>
4. 与Options API对比
特性 | Options API | Composition API |
---|---|---|
代码组织 | 按选项类型分组 | 按逻辑功能分组 |
类型支持 | 一般 | 优秀 |
逻辑复用 | Mixins/作用域插槽 | 自定义组合函数 |
学习曲线 | 平缓 | 较陡峭 |
适合场景 | 简单组件 | 复杂逻辑/大型应用 |
三、Pinia:下一代Vue状态管理
1. Pinia vs Vuex
Pinia是Vue官方推荐的状态管理库,相比Vuex有诸多优势:
-
更简单的API,去掉mutations概念
-
完整的TypeScript支持
-
模块化设计,无需嵌套模块
-
更轻量,打包体积更小
-
支持Composition API和Options API
2. 核心概念与基本使用
安装Pinia
npm install pinia
创建Store
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
},
async fetchData() {
const response = await fetch('/api/data')
this.data = await response.json()
}
},
getters: {
doubleCount: (state) => state.count * 2
}
})
在组件中使用
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>当前计数: {{ counter.count }}</div>
<div>双倍计数: {{ counter.doubleCount }}</div>
<button @click="counter.increment()">增加</button>
</template>
3. 高级特性
状态持久化
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// 在store中启用
export const useUserStore = defineStore('user', {
state: () => ({
user: null
}),
persist: true
})
模块化组织
// stores/index.js
import { useUserStore } from './user'
import { useCartStore } from './cart'
import { useProductStore } from './product'
export default {
user: useUserStore,
cart: useCartStore,
product: useProductStore
}
// 组件中使用
import stores from '@/stores'
const userStore = stores.user()
const cartStore = stores.cart()
四、Vue Router路由守卫实战
1. 路由守卫类型
Vue Router提供了多种路由守卫,用于控制导航:
-
全局守卫
-
beforeEach
-
beforeResolve
-
afterEach
-
-
路由独享守卫
-
beforeEnter
-
-
组件内守卫
-
beforeRouteEnter
-
beforeRouteUpdate
-
beforeRouteLeave
-
2. 典型应用场景
认证检查
// router.js
import { createRouter } from 'vue-router'
const router = createRouter({
// ...路由配置
})
router.beforeEach(async (to, from) => {
const isAuthenticated = await checkAuth()
// 需要登录但未登录
if (to.meta.requiresAuth && !isAuthenticated) {
return {
path: '/login',
query: { redirect: to.fullPath }
}
}
// 已登录但访问登录页
if (to.path === '/login' && isAuthenticated) {
return { path: '/' }
}
})
权限控制
router.beforeEach(async (to, from) => {
const userRole = await getUserRole()
const requiredRoles = to.meta.roles || []
if (requiredRoles.length && !requiredRoles.includes(userRole)) {
return { path: '/403' } // 无权限页面
}
})
页面访问统计
router.afterEach((to, from) => {
trackPageView(to.fullPath)
})
3. 组合式API中使用路由守卫
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// 当前路由变化但组件被复用时调用
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
await fetchData(to.params.id)
}
})
// 离开当前路由前调用
onBeforeRouteLeave((to, from) => {
if (formHasChanges.value) {
return confirm('您有未保存的更改,确定要离开吗?')
}
})
</script>
五、三大特性整合实战
1. 用户认证系统实现
认证Store
// stores/auth.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
import router from '@/router'
export const useAuthStore = defineStore('auth', () => {
const user = ref(null)
const token = ref(localStorage.getItem('token'))
async function login(credentials) {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
})
const data = await response.json()
user.value = data.user
token.value = data.token
localStorage.setItem('token', data.token)
router.push('/dashboard')
}
function logout() {
user.value = null
token.value = null
localStorage.removeItem('token')
router.push('/login')
}
return { user, token, login, logout }
})
路由配置
// router.js
import { createRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
routes: [
{
path: '/',
component: () => import('@/views/Home.vue'),
meta: { requiresAuth: true }
},
{
path: '/login',
component: () => import('@/views/Login.vue'),
meta: { guestOnly: true }
}
]
})
router.beforeEach((to) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.token) {
return { path: '/login', query: { redirect: to.fullPath } }
}
if (to.meta.guestOnly && auth.token) {
return { path: '/' }
}
})
登录组件
<script setup>
import { ref } from 'vue'
import { useAuthStore } from '@/stores/auth'
const auth = useAuthStore()
const form = ref({
email: '',
password: ''
})
async function handleSubmit() {
try {
await auth.login(form.value)
} catch (error) {
alert('登录失败: ' + error.message)
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="form.email" type="email" placeholder="邮箱">
<input v-model="form.password" type="password" placeholder="密码">
<button type="submit">登录</button>
</form>
</template>
六、性能优化与最佳实践
1. Composition API优化技巧
-
使用
computed
缓存计算结果 -
将复杂逻辑拆分为多个组合函数
-
使用
watchEffect
和watch
时注意清理副作用 -
对于大型组件,考虑使用
<script setup>
语法糖
2. Pinia状态管理最佳实践
-
按功能而非数据类型组织store
-
避免在store中直接操作DOM
-
使用getters派生状态,减少重复计算
-
对于复杂业务逻辑,使用actions封装
-
合理使用状态持久化
3. 路由守卫注意事项
-
避免在守卫中执行耗时同步操作
-
对于异步操作,确保返回Promise或使用async/await
-
考虑使用路由元信息(meta)来配置守卫行为
-
全局守卫中避免频繁的状态访问,可缓存必要数据
七、常见问题与解决方案
1. Composition API相关问题
Q: 如何在setup中访问this?
A: 在setup中不需要也不应该使用this,所有组件实例的访问都应通过参数或Composition API函数实现。
Q: 如何复用组件逻辑?
A: 将可复用逻辑提取为组合函数,返回需要暴露的响应式数据和方法。
2. Pinia使用问题
Q: 什么时候应该使用Pinia?
A: 当需要在多个组件间共享状态,或者状态逻辑比较复杂时使用。对于简单的父子组件通信,props和emit可能更合适。
Q: 如何调试Pinia状态?
A: 安装Vue Devtools,它提供了专门的Pinia面板用于状态调试。
3. 路由守卫问题
Q: 导航守卫中如何获取组件实例?
A: 在beforeRouteEnter
中可以通过next回调访问:
beforeRouteEnter(to, from, next) {
next(vm => {
// 通过vm访问组件实例
})
}
Q: 如何处理取消的导航?
A: 返回false或导航到其他路由,确保守卫函数有明确的返回值。
八、总结
Vue 3通过Composition API、Pinia和增强的Vue Router,为开发者提供了更强大、更灵活的工具集:
-
Composition API 改变了我们组织组件逻辑的方式,使代码更易于维护和复用
-
Pinia 作为新一代状态管理方案,简化了全局状态管理,提供了更好的开发体验
-
路由守卫 系统为应用提供了完善的导航控制能力
掌握这三大特性,你将能够构建更复杂、更健壮的Vue 3应用程序。随着Vue生态的不断发展,这些技术也将继续演进,建议持续关注官方文档和社区动态。