Vue 3 深度解析:Composition API、Pinia状态管理与路由守卫实战

发布于:2025-05-23 ⋅ 阅读:(24) ⋅ 点赞:(0)

一、Vue 3带来的革命性变化

Vue 3自2020年发布以来,已经成为现代前端开发的首选框架之一。相较于Vue 2,Vue 3在性能、开发体验和可维护性方面都有显著提升。本文将重点介绍Vue 3最核心的三大特性:Composition API、Pinia状态管理和Vue Router路由守卫,通过实战示例带你掌握这些技术的精髓。

Vue 3的主要改进

  1. 性能提升:重写了虚拟DOM,更新速度提升1.3-2倍

  2. Tree-shaking支持:打包体积减少41%

  3. Composition API:更好的逻辑组织和代码复用

  4. 更好的TypeScript支持:代码提示更完善

  5. 新的响应式系统:基于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提供了多种路由守卫,用于控制导航:

  1. 全局守卫

    • beforeEach

    • beforeResolve

    • afterEach

  2. 路由独享守卫

    • beforeEnter

  3. 组件内守卫

    • 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缓存计算结果

  • 将复杂逻辑拆分为多个组合函数

  • 使用watchEffectwatch时注意清理副作用

  • 对于大型组件,考虑使用<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,为开发者提供了更强大、更灵活的工具集:

  1. Composition API 改变了我们组织组件逻辑的方式,使代码更易于维护和复用

  2. Pinia 作为新一代状态管理方案,简化了全局状态管理,提供了更好的开发体验

  3. 路由守卫 系统为应用提供了完善的导航控制能力

掌握这三大特性,你将能够构建更复杂、更健壮的Vue 3应用程序。随着Vue生态的不断发展,这些技术也将继续演进,建议持续关注官方文档和社区动态。


网站公告

今日签到

点亮在社区的每一天
去签到