Vue3实战:Composition API、Pinia与Router核心概念与应用指南

发布时间:2026/7/24 16:05:46
Vue3实战:Composition API、Pinia与Router核心概念与应用指南 很多刚接触 Vue3 的同学会有这样的困惑明明官方文档都看了一遍Composition API、Pinia、Router 这些概念也大概知道是什么但一到实际项目就不知道怎么组织代码总感觉写出来的东西既不像 Vue2 那么直观又达不到 Vue3 应有的简洁和灵活。这其实不是你的问题而是学习路径的问题。Vue3 的核心价值不在于单个 API 的使用而在于它重新定义了前端开发的思考方式——从“选项式”的配置思维转向“组合式”的函数思维。今天我们就从实战角度把这三个核心概念真正吃透。1. 为什么说 Composition API 改变了 Vue 的编程范式很多人把 Composition API 简单理解为“把 data、methods、computed 写进 setup 函数里”这是最大的误解。Composition API 的本质是让你能用函数的方式来组织代码逻辑而不是被迫按照框架规定的选项结构来写。1.1 从“选项思维”到“函数思维”的转变在 Vue2 中一个功能相关的代码被分散在不同的选项里// Vue2 选项式 API export default { data() { return { count: 0, searchQuery: } }, computed: { filteredList() { return this.list.filter(item item.includes(this.searchQuery)) } }, methods: { increment() { this.count }, search() { // 搜索逻辑 } }, mounted() { this.fetchData() } }这种结构的问题在于当组件复杂时同一个功能的代码被分散在 data、methods、computed、mounted 等多个地方维护时需要上下滚动查看。Composition API 让你可以按功能而不是按选项来组织代码// Vue3 组合式 API import { ref, computed, onMounted } from vue export default { setup() { // 计数器功能 const count ref(0) const increment () count.value // 搜索功能 const searchQuery ref() const list ref([]) const filteredList computed(() list.value.filter(item item.includes(searchQuery.value)) ) const fetchData async () { // 获取数据 } onMounted(fetchData) return { count, increment, searchQuery, filteredList } } }1.2 逻辑复用的革命自定义组合函数Composition API 最强大的地方在于可以提取可复用的逻辑。比如我们可以把搜索功能抽象成一个独立的组合函数// composables/useSearch.js import { ref, computed } from vue export function useSearch(initialList []) { const searchQuery ref() const list ref(initialList) const filteredList computed(() list.value.filter(item item.toLowerCase().includes(searchQuery.value.toLowerCase()) ) ) const updateList (newList) { list.value newList } return { searchQuery, filteredList, updateList } }然后在多个组件中复用// 组件A import { useSearch } from /composables/useSearch export default { setup() { const { searchQuery, filteredList, updateList } useSearch([apple, banana]) return { searchQuery, filteredList } } } // 组件B import { useSearch } from /composables/useSearch export default { setup() { const { searchQuery, filteredList, updateList } useSearch([cat, dog]) return { searchQuery, filteredList } } }这种复用方式比 Vue2 的 mixin 更安全、更灵活因为每个组合函数都有独立的作用域不会出现命名冲突的问题。1.3 响应式系统的深度理解很多人对 ref 和 reactive 的使用场景感到困惑其实选择原则很简单ref用于基本类型string、number、boolean和需要保持引用的对象reactive用于纯对象且不需要重新赋值的场景import { ref, reactive } from vue // 适合用 ref 的情况 const count ref(0) const user ref({ name: John, age: 30 }) // 适合用 reactive 的情况 const form reactive({ username: , password: , remember: false }) // 修改方式的不同 count.value // ref 需要 .value form.username new name // reactive 直接赋值实际项目中我建议优先使用 ref因为它在组合函数中返回时更安全不容易丢失响应性。2. Pinia 不仅仅是 Vuex 的替代品Pinia 被很多人理解为 Vuex5但实际上它的设计理念比 Vuex 更先进。Pinia 的核心优势在于更好的 TypeScript 支持、更简洁的 API 设计和与 Composition API 的无缝集成。2.1 Store 设计的核心原则一个良好的 Store 应该遵循单一职责原则。不要创建一个巨大的 Store 来管理所有状态而是按业务模块拆分// stores/user.js import { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ userInfo: null, permissions: [], isLoggedIn: false }), getters: { userName: (state) state.userInfo?.name || , hasPermission: (state) (permission) state.permissions.includes(permission) }, actions: { async login(credentials) { const userData await api.login(credentials) this.userInfo userData.user this.permissions userData.permissions this.isLoggedIn true }, logout() { this.userInfo null this.permissions [] this.isLoggedIn false } } })// stores/app.js import { defineStore } from pinia export const useAppStore defineStore(app, { state: () ({ theme: light, sidebarCollapsed: false, loading: false }), actions: { toggleTheme() { this.theme this.theme light ? dark : light }, setLoading(loading) { this.loading loading } } })2.2 Option Store 与 Setup Store 的选择Pinia 支持两种定义 Store 的方式各有适用场景Option Store适合大多数场景export const useCounterStore defineStore(counter, { state: () ({ count: 0 }), getters: { double: (state) state.count * 2 }, actions: { increment() { this.count } } })Setup Store需要更复杂逻辑时export const useCounterStore defineStore(counter, () { const count ref(0) const double computed(() count.value * 2) function increment() { count.value } return { count, double, increment } })我建议新手先从 Option Store 开始等熟悉 Pinia 后再根据需要使用 Setup Store。2.3 Store 之间的通信和组合多个 Store 之间需要通信时不要直接相互导入和调用而是通过事件或高层 Store 来协调// stores/order.js export const useOrderStore defineStore(order, { actions: { async submitOrder() { // 提交订单前检查用户权限 const userStore useUserStore() if (!userStore.hasPermission(create_order)) { throw new Error(无权限创建订单) } // 设置加载状态 const appStore useAppStore() appStore.setLoading(true) try { // 提交订单逻辑 await api.submitOrder(this.orderData) } finally { appStore.setLoading(false) } } } })2.4 持久化存储的最佳实践对于需要持久化的数据如用户登录状态、主题设置建议使用 pinia-plugin-persistedstateimport { createPinia } from pinia import piniaPluginPersistedstate from pinia-plugin-persistedstate const pinia createPinia() pinia.use(piniaPluginPersistedstate) // Store 配置 export const useUserStore defineStore(user, { state: () ({ token: , userInfo: null }), persist: { key: user-data, paths: [token] // 只持久化 token } })3. Vue Router 4 的现代化路由管理Vue Router 4 为 Vue3 提供了更强大的路由能力特别是在类型安全和组合式 API 集成方面。3.1 路由配置的模块化组织大型项目的路由配置应该按模块拆分// router/index.js import { createRouter, createWebHistory } from vue-router const routes [ { path: /, component: () import(/layouts/MainLayout.vue), children: [ ...homeRoutes, ...userRoutes, ...productRoutes ] }, { path: /auth, component: () import(/layouts/AuthLayout.vue), children: authRoutes } ] export const router createRouter({ history: createWebHistory(), routes })// router/routes/user.js export const userRoutes [ { path: profile, name: user-profile, component: () import(/views/UserProfile.vue), meta: { requiresAuth: true } }, { path: settings, name: user-settings, component: () import(/views/UserSettings.vue), meta: { requiresAuth: true } } ]3.2 组合式 API 下的路由使用在组合式 API 中路由相关的操作更加直观import { useRoute, useRouter } from vue-router import { watch } from vue export default { setup() { const route useRoute() const router useRouter() // 获取路由参数 const userId computed(() route.params.id) // 响应路由参数变化 watch(() route.params.id, (newId) { fetchUserData(newId) }) // 编程式导航 const goToUserProfile (id) { router.push({ name: user-profile, params: { id } }) } // 替换当前路由不保留历史记录 const replaceToHome () { router.replace({ name: home }) } return { userId, goToUserProfile, replaceToHome } } }3.3 路由守卫的现代化写法路由守卫应该保持简洁和可维护// router/guards.js export function setupRouterGuards(router) { // 全局前置守卫 router.beforeEach((to, from) { const userStore useUserStore() // 检查是否需要登录 if (to.meta.requiresAuth !userStore.isLoggedIn) { return { name: login, query: { redirect: to.fullPath } } } // 页面标题设置 if (to.meta.title) { document.title ${to.meta.title} - 我的应用 } }) // 全局后置钩子 router.afterEach((to, from) { // 页面访问统计 trackPageView(to.fullPath) }) }3.4 动态路由的高级用法对于权限管理系统动态路由是必备功能// 动态添加路由 export function addDynamicRoutes(router, userPermissions) { const dynamicRoutes generateRoutesBasedOnPermissions(userPermissions) dynamicRoutes.forEach(route { router.addRoute(route) }) } // 基于权限生成路由 function generateRoutesBasedOnPermissions(permissions) { const routes [] if (permissions.includes(admin)) { routes.push({ path: /admin, component: () import(/views/AdminDashboard.vue), meta: { requiresAdmin: true } }) } if (permissions.includes(report_view)) { routes.push({ path: /reports, component: () import(/views/Reports.vue) }) } return routes }4. 三者的协同构建可维护的 Vue3 应用架构单独掌握每个技术点还不够关键是要理解它们如何协同工作。4.1 项目目录结构的最佳实践src/ ├── components/ # 通用组件 │ ├── ui/ # 基础UI组件 │ └── business/ # 业务组件 ├── composables/ # 组合函数 │ ├── useApi.js │ ├── useForm.js │ └── usePagination.js ├── stores/ # Pinia Store │ ├── user.js │ ├── app.js │ └── product.js ├── router/ # 路由配置 │ ├── index.js │ ├── routes/ │ └── guards.js ├── views/ # 页面组件 ├── utils/ # 工具函数 └── api/ # API 接口4.2 数据流的最佳实践在 Vue3 应用中清晰的数据流非常重要组件内部状态使用 ref/reactive组件间共享状态使用 Props/Events 或 provide/inject全局应用状态使用 Pinia Store服务端状态使用 TanStack Query 或 SWR复杂场景// 组件示例 export default { setup() { // 本地状态 const localFilter ref() // 从 Store 获取全局状态 const userStore useUserStore() const { userInfo } storeToRefs(userStore) // 使用组合函数 const { data, loading, error } useApi(/api/data) // 路由相关 const route useRoute() return { localFilter, userInfo, data, loading } } }4.3 错误处理和加载状态管理良好的错误处理能显著提升用户体验// composables/useAsync.js export function useAsync(fn) { const loading ref(false) const error ref(null) const data ref(null) const execute async (...args) { loading.value true error.value null try { data.value await fn(...args) } catch (err) { error.value err // 统一错误处理 handleError(err) } finally { loading.value false } } return { loading, error, data, execute } } // 在组件中使用 export default { setup() { const { loading, error, data, execute } useAsync(api.getUserList) onMounted(() execute()) return { loading, error, userList: data } } }4.4 性能优化要点Vue3 应用性能优化的关键点组件懒加载const UserProfile defineAsyncComponent(() import(/views/UserProfile.vue) )状态按需加载// 只在需要时加载 Store const userStore useUserStore() if (needUserInfo) { userStore.fetchUserInfo() }计算属性缓存const expensiveValue computed(() { // 复杂计算 return heavyCalculation(data.value) })监听器优化// 避免深层监听 watch(() props.item.id, (newId) { // 只在 id 变化时执行 }, { immediate: true })Vue3 的这套技术栈真正强大的地方在于它提供了一套完整的思想体系而不仅仅是一堆 API。当你真正理解组合式思维后会发现代码的组织方式变得更加自然和可预测。记住好的架构不是一开始就设计出来的而是在不断重构中逐步演化出来的。先从小的组合函数开始逐步构建出适合自己项目的模式。