Vue3 Composition API、Pinia与Vue Router实战:构建复杂单页应用
2026/7/24 9:47:28 网站建设 项目流程

Vue3 已经成为现代前端开发的主流选择,特别是其 Composition API、Pinia 状态管理和 Vue Router 路由系统的组合,让开发者能够构建更复杂、更易维护的应用。这次我们深入实战,看看如何真正掌握这三个核心工具。

从实际项目经验来看,很多开发者在使用 Vue3 时面临的主要挑战不是基础语法,而是如何将 Composition API 的响应式编程思维、Pinia 的状态管理架构和 Vue Router 的路由控制有机结合起来。本文将通过完整的项目示例,带你从环境搭建到高级功能实现,重点解决实际开发中的痛点问题。

1. 核心能力速览

能力项说明
技术栈Vue3 + Composition API + Pinia + Vue Router
主要功能响应式状态管理、路由控制、组件通信、TypeScript 支持
开发体验热重载、TypeScript 自动补全、DevTools 集成
适合场景中大型单页应用、需要状态管理的复杂项目、Vue2 升级迁移
学习门槛有 Vue 基础即可上手,需要适应 Composition API 思维

2. 适用场景与使用边界

Vue3 的这套技术组合特别适合需要复杂状态管理和路由控制的单页应用。比如后台管理系统、数据可视化平台、电商应用等需要多页面状态共享的场景。

对于小型项目或简单的展示页面,可能不需要引入完整的状态管理,直接使用 Composition API 的reactiveref就能满足需求。Pinia 的优势在于提供标准化的状态管理模式,便于团队协作和长期维护。

需要注意的是,虽然 Vue3 对 Vue2 有很好的兼容性,但在企业级项目中,建议直接使用 Composition API 的写法,避免混合使用选项式 API 和组合式 API 导致的代码风格不统一。

3. 环境准备与前置条件

在开始实战之前,确保你的开发环境满足以下要求:

Node.js 版本: 推荐使用 Node.js 16.x 或以上版本,可以使用node -v检查当前版本。

包管理器: npm、yarn 或 pnpm 都可以,本文示例使用 npm。

编辑器配置: 推荐使用 VSCode,并安装 Volar 扩展来获得更好的 Vue3 开发体验。

浏览器要求: 现代浏览器都支持 Vue3,如果需要兼容旧版浏览器,可能需要额外的 polyfill。

检查环境是否就绪:

# 检查 Node.js 版本 node -v # 检查 npm 版本 npm -v # 创建项目目录 mkdir vue3-advanced-project cd vue3-advanced-project

4. 项目初始化与依赖安装

使用 Vite 创建 Vue3 项目是目前最高效的方式,相比 Vue CLI 有更快的启动速度和更好的开发体验。

# 使用 npm 创建项目 npm create vue@latest vue3-advanced-project # 进入项目目录 cd vue3-advanced-project # 安装依赖 npm install # 安装 Pinia 和 Router npm install pinia vue-router@4 # 启动开发服务器 npm run dev

项目创建完成后,需要配置 Pinia 和 Vue Router。首先修改main.js

import { createApp } from 'vue' import { createPinia } from 'pinia' import { createRouter, createWebHistory } from 'vue-router' import App from './App.vue' // 创建 Pinia 实例 const pinia = createPinia() // 创建路由实例 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', name: 'Home', component: () => import('./views/Home.vue') }, { path: '/about', name: 'About', component: () => import('./views/About.vue') } ] }) const app = createApp(App) app.use(pinia) app.use(router) app.mount('#app')

5. Composition API 深度实战

Composition API 是 Vue3 的核心特性,它提供了更灵活的代码组织方式。我们先从基础用法开始,逐步深入到高级模式。

5.1 响应式数据管理

<template> <div> <h2>用户信息</h2> <p>姓名: {{ user.name }}</p> <p>年龄: {{ user.age }}</p> <p>计算属性: {{ doubledAge }}</p> <button @click="increaseAge">增加年龄</button> </div> </template> <script setup> import { ref, reactive, computed, watch } from 'vue' // 使用 ref 管理基本类型数据 const count = ref(0) // 使用 reactive 管理对象类型数据 const user = reactive({ name: '张三', age: 25, email: 'zhangsan@example.com' }) // 计算属性 const doubledAge = computed(() => user.age * 2) // 监听器 watch( () => user.age, (newAge, oldAge) => { console.log(`年龄从 ${oldAge} 变为 ${newAge}`) } ) // 方法 const increaseAge = () => { user.age++ } </script>

5.2 组合式函数封装

Composition API 的真正威力在于可以封装可复用的逻辑:

// composables/useCounter.js import { ref, computed } 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 doubled = computed(() => count.value * 2) return { count, increment, decrement, reset, doubled } }

在组件中使用:

<script setup> import { useCounter } from '@/composables/useCounter' const { count, increment, doubled } = useCounter(10) </script> <template> <div> <p>计数: {{ count }}</p> <p>双倍: {{ doubled }}</p> <button @click="increment">增加</button> </div> </template>

6. Pinia 状态管理实战

Pinia 是 Vue3 官方推荐的状态管理库,相比 Vuex 有更简单的 API 和更好的 TypeScript 支持。

6.1 创建 Store

// stores/userStore.js import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ user: null, isLoggedIn: false, token: null }), getters: { userName: (state) => state.user?.name || '未登录', isAdmin: (state) => state.user?.role === 'admin' }, actions: { async login(credentials) { try { // 模拟 API 调用 const response = await api.login(credentials) this.user = response.user this.token = response.token this.isLoggedIn = true return response } catch (error) { throw error } }, logout() { this.user = null this.token = null this.isLoggedIn = false }, updateUserProfile(updates) { if (this.user) { this.user = { ...this.user, ...updates } } } } })

6.2 在组件中使用 Store

<template> <div> <div v-if="userStore.isLoggedIn"> <h3>欢迎, {{ userStore.userName }}</h3> <p v-if="userStore.isAdmin">管理员权限</p> <button @click="handleLogout">退出登录</button> </div> <div v-else> <button @click="showLogin = true">登录</button> </div> <LoginModal v-if="showLogin" @close="showLogin = false" /> </div> </template> <script setup> import { useUserStore } from '@/stores/userStore' import { storeToRefs } from 'pinia' const userStore = useUserStore() // 使用 storeToRefs 保持响应式 const { userName, isAdmin } = storeToRefs(userStore) const showLogin = ref(false) const handleLogout = () => { userStore.logout() } </script>

6.3 Pinia 持久化存储

在实际项目中,通常需要将状态持久化到 localStorage:

// plugins/persistence.js export function createPersistedState() { return (context) => { const { store } = context // 从 localStorage 恢复状态 const stored = localStorage.getItem(`pinia-${store.$id}`) if (stored) { store.$patch(JSON.parse(stored)) } // 监听状态变化并保存 store.$subscribe((mutation, state) => { localStorage.setItem(`pinia-${store.$id}`, JSON.stringify(state)) }) } } // 在 main.js 中注册 import { createPersistedState } from './plugins/persistence' const pinia = createPinia() pinia.use(createPersistedState())

7. Vue Router 路由管理实战

Vue Router 4 为 Vue3 提供了强大的路由功能,支持路由守卫、懒加载等特性。

7.1 路由配置进阶

// router/index.js import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', name: 'Home', component: () => import('@/views/Home.vue'), meta: { requiresAuth: true, title: '首页' } }, { path: '/login', name: 'Login', component: () => import('@/views/Login.vue'), meta: { title: '登录', hideHeader: true } }, { path: '/user/:id', name: 'UserProfile', component: () => import('@/views/UserProfile.vue'), props: true, // 将路由参数作为 props 传递 meta: { requiresAuth: true } }, { path: '/:pathMatch(.*)*', name: 'NotFound', component: () => import('@/views/NotFound.vue') } ] const router = createRouter({ history: createWebHistory(), routes, scrollBehavior(to, from, savedPosition) { // 滚动行为控制 if (savedPosition) { return savedPosition } else { return { top: 0 } } } })

7.2 路由守卫实现

// 全局前置守卫 router.beforeEach((to, from, next) => { const userStore = useUserStore() // 设置页面标题 if (to.meta.title) { document.title = to.meta.title } // 检查是否需要认证 if (to.meta.requiresAuth && !userStore.isLoggedIn) { next({ name: 'Login', query: { redirect: to.fullPath } }) } else { next() } }) // 全局后置钩子 router.afterEach((to, from) => { // 可以在这里进行页面统计等操作 console.log(`从 ${from.name} 导航到 ${to.name}`) })

7.3 路由参数和查询参数处理

<template> <div> <h2>用户详情: {{ userId }}</h2> <p>搜索关键词: {{ searchKeyword }}</p> </div> </template> <script setup> import { useRoute, useRouter } from 'vue-router' const route = useRoute() const router = useRouter() // 获取路由参数 const userId = computed(() => route.params.id) // 获取查询参数 const searchKeyword = computed(() => route.query.keyword || '') // 编程式导航 const goToUserSettings = () => { router.push({ name: 'UserSettings', params: { id: userId.value }, query: { tab: 'profile' } }) } // 替换当前路由(不留下历史记录) const replaceRoute = () => { router.replace({ name: 'Home' }) } </script>

8. 三者的协同工作模式

在实际项目中,Composition API、Pinia 和 Vue Router 需要协同工作。下面是一个完整的示例:

8.1 用户认证流程集成

<!-- views/Login.vue --> <template> <div class="login-container"> <form @submit.prevent="handleLogin"> <input v-model="form.username" placeholder="用户名"> <input v-model="form.password" type="password" placeholder="密码"> <button type="submit" :disabled="loading">登录</button> </form> </div> </template> <script setup> import { ref } from 'vue' import { useRouter, useRoute } from 'vue-router' import { useUserStore } from '@/stores/userStore' const router = useRouter() const route = useRoute() const userStore = useUserStore() const form = ref({ username: '', password: '' }) const loading = ref(false) const handleLogin = async () => { loading.value = true try { await userStore.login(form.value) // 登录成功后跳转 const redirect = route.query.redirect || '/' router.push(redirect) } catch (error) { console.error('登录失败:', error) } finally { loading.value = false } } </script>

8.2 路由级状态管理

在某些场景下,需要在路由级别管理状态:

// composables/useRouteState.js import { ref, watch } from 'vue' import { useRoute } from 'vue-router' export function useRouteState(key, defaultValue) { const route = useRoute() const state = ref(defaultValue) // 从路由查询参数初始化状态 if (route.query[key]) { state.value = route.query[key] } // 状态变化时更新路由 watch(state, (newValue) => { const query = { ...route.query } if (newValue !== defaultValue && newValue !== '') { query[key] = newValue } else { delete query[key] } router.replace({ query }) }) return state }

在组件中使用:

<script setup> import { useRouteState } from '@/composables/useRouteState' // 这个状态会自动同步到路由查询参数 const searchQuery = useRouteState('q', '') const currentPage = useRouteState('page', 1) </script>

9. 性能优化实践

9.1 组件懒加载

// 路由懒加载 const routes = [ { path: '/admin', component: () => import(/* webpackChunkName: "admin" */ '@/views/Admin.vue') } ] // 组件懒加载 const HeavyComponent = defineAsyncComponent(() => import('@/components/HeavyComponent.vue') )

9.2 状态管理优化

// 使用 computed 优化性能 const expensiveValue = computed(() => { return heavyCalculation(store.largeArray) }) // 避免不必要的响应式 const nonReactiveData = markRaw(largeStaticObject)

9.3 路由切换优化

<template> <router-view v-slot="{ Component }"> <keep-alive :include="cachedComponents"> <component :is="Component" /> </keep-alive> </router-view> </template> <script setup> import { ref } from 'vue' const cachedComponents = ref(['Home', 'UserProfile']) </script>

10. TypeScript 集成实战

Vue3 对 TypeScript 的支持非常完善,下面是类型安全的实践:

10.1 Pinia Store 类型定义

// types/user.ts export interface User { id: number name: string email: string role: 'admin' | 'user' } export interface AuthState { user: User | null isLoggedIn: boolean token: string | null } // stores/userStore.ts import { defineStore } from 'pinia' import type { User, AuthState } from '@/types/user' export const useUserStore = defineStore('user', { state: (): AuthState => ({ user: null, isLoggedIn: false, token: null }), getters: { userName: (state): string => state.user?.name || '未登录', isAdmin: (state): boolean => state.user?.role === 'admin' }, actions: { async login(credentials: { username: string; password: string }) { // 类型安全的实现 } } })

10.2 组件 Props 类型定义

<script setup lang="ts"> interface Props { title: string count?: number items: string[] } const props = withDefaults(defineProps<Props>(), { count: 0 }) const emit = defineEmits<{ (e: 'update:count', value: number): void (e: 'submit', data: FormData): void }>() </script>

11. 测试策略

11.1 组件测试

// tests/component.spec.js import { mount } from '@vue/test-utils' import { createPinia } from 'pinia' import Component from '@/components/MyComponent.vue' describe('MyComponent', () => { it('renders correctly', () => { const pinia = createPinia() const wrapper = mount(Component, { global: { plugins: [pinia] } }) expect(wrapper.html()).toMatchSnapshot() }) })

11.2 Store 测试

// tests/store.spec.js import { setActivePinia, createPinia } from 'pinia' import { useUserStore } from '@/stores/userStore' describe('User Store', () => { beforeEach(() => { setActivePinia(createPinia()) }) it('should login successfully', async () => { const store = useUserStore() await store.login({ username: 'test', password: 'test' }) expect(store.isLoggedIn).toBe(true) }) })

12. 常见问题与解决方案

12.1 响应式丢失问题

// 错误做法:响应式丢失 const user = reactive({ name: '张三' }) const newUser = user // 失去响应式 // 正确做法:使用 toRefs 或保持引用 const { name } = toRefs(user) // 或直接使用原响应式对象

12.2 路由缓存问题

<template> <router-view v-slot="{ Component, route }"> <keep-alive> <component :is="Component" :key="route.meta.usePathKey ? route.path : undefined" /> </keep-alive> </router-view> </template>

12.3 Store 状态持久化冲突

// 解决持久化状态冲突 export const useUserStore = defineStore('user', { state: () => ({ // 敏感信息不持久化 token: null, // 可以持久化的信息 preferences: loadPreferences() }), actions: { logout() { this.token = null // 保留用户偏好设置 } } })

13. 项目结构最佳实践

推荐的项目结构:

src/ ├── components/ # 可复用组件 │ ├── ui/ # 基础UI组件 │ └── business/ # 业务组件 ├── views/ # 页面组件 ├── stores/ # Pinia stores ├── router/ # 路由配置 ├── composables/ # 组合式函数 ├── utils/ # 工具函数 ├── types/ # TypeScript 类型定义 ├── assets/ # 静态资源 └── plugins/ # 插件配置

14. 部署与生产环境优化

14.1 构建配置

// vite.config.js export default defineConfig({ build: { rollupOptions: { output: { manualChunks: { vendor: ['vue', 'pinia', 'vue-router'], ui: ['element-plus', 'vant'] } } } } })

14.2 环境变量配置

// .env.production VITE_API_BASE=https://api.example.com VITE_APP_TITLE=生产环境 // 在代码中使用 const apiBase = import.meta.env.VITE_API_BASE

通过本文的实战演练,你应该能够掌握 Vue3 的核心技术栈。重点在于理解 Composition API 的响应式编程思维,合理使用 Pinia 进行状态管理,以及利用 Vue Router 实现复杂的路由控制。在实际项目中,这三者的协同工作能够显著提升开发效率和代码质量。

建议从一个小项目开始实践,逐步应用这些技术点。遇到问题时,可以回看对应的章节,大多数常见问题都有相应的解决方案。随着经验的积累,你会越来越熟练地运用这些工具构建复杂的 Vue3 应用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询