Vue3 Composition API、Pinia与Vue Router实战:构建复杂单页应用
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 的reactive或ref就能满足需求。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-project4. 项目初始化与依赖安装使用 Vite 创建 Vue3 项目是目前最高效的方式相比 Vue CLI 有更快的启动速度和更好的开发体验。# 使用 npm 创建项目 npm create vuelatest vue3-advanced-project # 进入项目目录 cd vue3-advanced-project # 安装依赖 npm install # 安装 Pinia 和 Router npm install pinia vue-router4 # 启动开发服务器 npm run dev项目创建完成后需要配置 Pinia 和 Vue Router。首先修改main.jsimport { 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 clickincreaseAge增加年龄/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: zhangsanexample.com }) // 计算属性 const doubledAge computed(() user.age * 2) // 监听器 watch( () user.age, (newAge, oldAge) { console.log(年龄从 ${oldAge} 变为 ${newAge}) } ) // 方法 const increaseAge () { user.age } /script5.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 clickincrement增加/button /div /template6. 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 在组件中使用 Storetemplate div div v-ifuserStore.isLoggedIn h3欢迎, {{ userStore.userName }}/h3 p v-ifuserStore.isAdmin管理员权限/p button clickhandleLogout退出登录/button /div div v-else button clickshowLogin true登录/button /div LoginModal v-ifshowLogin closeshowLogin 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() } /script6.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 }) } /script8. 三者的协同工作模式在实际项目中Composition API、Pinia 和 Vue Router 需要协同工作。下面是一个完整的示例8.1 用户认证流程集成!-- views/Login.vue -- template div classlogin-container form submit.preventhandleLogin input v-modelform.username placeholder用户名 input v-modelform.password typepassword placeholder密码 button typesubmit :disabledloading登录/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 } } /script8.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) /script9. 性能优化实践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 :includecachedComponents component :isComponent / /keep-alive /router-view /template script setup import { ref } from vue const cachedComponents ref([Home, UserProfile]) /script10. 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 langts interface Props { title: string count?: number items: string[] } const props withDefaults(definePropsProps(), { count: 0 }) const emit defineEmits{ (e: update:count, value: number): void (e: submit, data: FormData): void }() /script11. 测试策略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 :isComponent :keyroute.meta.usePathKey ? route.path : undefined / /keep-alive /router-view /template12.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_BASEhttps://api.example.com VITE_APP_TITLE生产环境 // 在代码中使用 const apiBase import.meta.env.VITE_API_BASE通过本文的实战演练你应该能够掌握 Vue3 的核心技术栈。重点在于理解 Composition API 的响应式编程思维合理使用 Pinia 进行状态管理以及利用 Vue Router 实现复杂的路由控制。在实际项目中这三者的协同工作能够显著提升开发效率和代码质量。建议从一个小项目开始实践逐步应用这些技术点。遇到问题时可以回看对应的章节大多数常见问题都有相应的解决方案。随着经验的积累你会越来越熟练地运用这些工具构建复杂的 Vue3 应用。

相关新闻

Claude Code Agent Teams:AI辅助编程的团队协作新模式

Claude Code Agent Teams:AI辅助编程的团队协作新模式

1. 项目概述Claude Code Agent Teams是当前AI辅助编程领域最具创新性的协作模式之一。作为一名长期关注AI开发工具的技术博主,我花了三个月时间深度测试了这种团队协作方式,发现它能将开发效率提升300%以上。不同于传统的单AI助手模式,这种团…

2026/10/12 2:23:41 阅读更多 →
RAG系统效果不佳的三大核心问题与优化方案

RAG系统效果不佳的三大核心问题与优化方案

1. RAG效果不佳的三大核心症结 检索增强生成(RAG)系统在实际应用中常出现效果不达预期的情况,但问题往往不在大模型本身。根据我在多个企业级RAG项目中的实施经验,90%的效能瓶颈集中在以下三个关键环节: 1.1 数据预处…

2026/10/13 18:41:49 阅读更多 →
RAG技术优化实战:检索增强生成系统架构与性能提升

RAG技术优化实战:检索增强生成系统架构与性能提升

1. RAG技术全景解析:从基础架构到行业痛点RAG(Retrieval-Augmented Generation)技术正在重塑知识密集型AI应用的开发范式。这种将检索系统与生成模型相结合的方法,本质上构建了一个动态知识库系统——它不像传统语言模型那样依赖训…

2026/10/9 16:13:55 阅读更多 →

最新新闻

Grad-CAM原理解析与工业级实现指南

Grad-CAM原理解析与工业级实现指南

1. 这不是“画个热力图”那么简单:Grad-CAM到底在解决什么问题?Grad-CAM可视化,这五个字最近在模型可解释性讨论里出现频率极高,但很多人一上手就卡在“为什么我的热力图全是噪点”“为什么关键区域没亮起来”“为什么不同层的图完…

2026/10/15 15:22:08 阅读更多 →
Vue3源码解析:从effect看watch与computed的设计与调度

Vue3源码解析:从effect看watch与computed的设计与调度

Vue 3 的watch和computed这两条 API,可能是绝大多数项目里使用率最高的响应式能力,但也是误解最多的一对。平时查资料,大家见到的多半是用法层面的总结:computed 用于同步派生状态、watch 用于执行副作用、能用 computed 就别用 w…

2026/10/15 15:22:08 阅读更多 →
碰碰好评卡功能模块开发实战指南 附全流程实现思路与踩坑汇总

碰碰好评卡功能模块开发实战指南 附全流程实现思路与踩坑汇总

碰碰好评卡功能模块开发实战指南 附全流程实现思路与踩坑汇总 在电商、本地生活、上门服务等各类线上业务场景中,用户评价是影响商品/服务转化的核心指标,碰碰好评卡作为引导用户主动完成评价的核心功能模块,需要兼顾触发灵活性、多端兼容性、…

2026/10/15 15:22:08 阅读更多 →
ZooKeeper核心机制精讲:数据模型、Watch与Leader选举

ZooKeeper核心机制精讲:数据模型、Watch与Leader选举

1. 为什么分布式系统里必须有ZooKeeper这样的角色早些年我做分布式系统的时候,最头疼的事情不是接口怎么写,而是几台机器之间怎么“商量事儿”。比如一个服务一共开了三个实例,数据库连接池一共 60 个连接,分配 30 个给 A、20 个给…

2026/10/15 15:22:08 阅读更多 →
okhttputils 使用指南:基于 OkHttp 3.3.1 的 Android 网络请求封装库完整实战

okhttputils 使用指南:基于 OkHttp 3.3.1 的 Android 网络请求封装库完整实战

网络移动开发 【免费下载链接】okhttputils [停止维护]okhttp的辅助类 项目地址: https://gitcode.com/gh_mirrors/ok/okhttputils 点击查看 免费下载 本文是一份围绕开源仓库 okhttputils 的完整使用指南。okhttputils 是一套对 Square OkHttp 的辅助封装类&#…

2026/10/15 15:22:08 阅读更多 →
基于单片机的电阻炉温控系统:DS18B20与PID调参实战

基于单片机的电阻炉温控系统:DS18B20与PID调参实战

简介:基于单片机的电阻炉温控制系统设计,是一套面向电子、自动化专业学生及嵌入式初学者的完整课程设计/毕设方案,涵盖DS18B20数字测温、单片机温度曲线动态跟踪和上位机通信三大环节。方案从硬件原理图、下位机控制程序到VB上位机界面均有实…

2026/10/15 15:21:06 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →