路由与Pinia集成
路由和Pinia是Vue.js生态中两个核心工具,前者管理页面导航,后者处理全局状态。将它们结合使用可以构建更高效的单页应用。
路由基础配置
在Vue项目中安装vue-router和Pinia:
npm install vue-router pinia
创建基础路由文件通常放在src/router/index.js
:
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
Pinia存储创建
典型的Pinia存储模块示例:
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
lastRoute: null
}),
actions: {
increment() {
this.count++
},
setLastRoute(routeName) {
this.lastRoute = routeName
}
}
})
路由守卫集成
在导航守卫中使用Pinia存储:
// router/index.js
import { useCounterStore } from '@/stores/counter'
router.beforeEach((to, from) => {
const store = useCounterStore()
store.setLastRoute(from.name)
if (to.meta.requiresAuth && !store.isAuthenticated) {
return '/login'
}
})
组件内访问路由状态
在组件中同时使用路由和Pinia:
<script setup>
import { useRoute } from 'vue-router'
import { useCounterStore } from '@/stores/counter'
const route = useRoute()
const counterStore = useCounterStore()
// 响应式获取当前路由参数
const userId = computed(() => route.params.id)
// 修改存储状态
function incrementWithRoute() {
counterStore.increment()
console.log('Current route:', route.fullPath)
}
</script>
<template>
<div>
<p>User ID: {{ userId }}</p>
<button @click="incrementWithRoute">Increment</button>
</div>
</template>
动态路由与存储同步
实现基于存储状态的动态路由加载:
// router/index.js
export function setupDynamicRoutes(router, store) {
store.$onAction(({ name, after }) => {
if (name === 'loadUserPermissions') {
after(() => {
const routes = store.permissions.map(perm => ({
path: `/${perm}`,
component: () => import(`@/views/${perm}View.vue`)
}))
routes.forEach(route => router.addRoute(route))
})
}
})
}
路由元信息与存储
利用路由元信息增强状态管理:
const routes = [
{
path: '/admin',
meta: {
requiresStore: 'admin',
storeAction: 'fetchAdminData'
}
}
]
router.beforeEach(async (to) => {
if (to.meta.requiresStore) {
const store = useStore()
await store[to.meta.storeAction]()
}
})
服务端渲染考虑
在SSR环境中需要注意的点:
// 在entry-server.js中
export default async function (context) {
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
const router = createRouter()
// 同步服务器端状态
if (context.piniaState) {
pinia.state.value = context.piniaState
}
await router.push(context.url)
await router.isReady()
return { app, router, pinia }
}
测试策略
编写测试时的实用模式:
import { setActivePinia, createPinia } from 'pinia'
import { createRouter, createWebHistory } from 'vue-router'
import { mount } from '@vue/test-utils'
test('uses pinia store with router', async () => {
setActivePinia(createPinia())
const router = createRouter({
history: createWebHistory(),
routes: []
})
const wrapper = mount(Component, {
global: {
plugins: [router]
}
})
await router.push('/test-route')
expect(wrapper.vm.$route.path).toBe('/test-route')
})
性能优化技巧
减少不必要的状态加载:
router.beforeEach((to) => {
if (to.meta.shouldPrefetch) {
const store = useStore()
// 仅当数据不存在时才加载
if (!store.isDataLoaded) {
store.loadInitialData()
}
}
})
类型安全集成
使用TypeScript增强类型检查:
// types/router.d.ts
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
requiresStore?: string
storeAction?: keyof typeof useStore
}
}
// stores/index.ts
export const useStore = defineStore('main', {
actions: {
fetchAdminData(): Promise<void> {
// 实现逻辑
}
}
})
错误处理模式
统一处理导航错误:
router.onError((error) => {
const store = useErrorStore()
store.logNavigationError(error)
if (isCriticalError(error)) {
router.push('/system-error')
}
})
路由懒加载优化
结合Pinia实现智能预加载:
// 在存储中跟踪路由加载状态
const useRouteStore = defineStore('routes', {
state: () => ({
loadedRoutes: new Set()
}),
actions: {
markRouteLoaded(routeName) {
this.loadedRoutes.add(routeName)
}
}
})
// 包装懒加载组件
function smartImport(routeName) {
const routeStore = useRouteStore()
return {
component: import(`@/views/${routeName}.vue`),
loadingComponent: routeStore.loadedRoutes.has(routeName)
? null
: LoadingSpinner
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:路由错误处理改进
下一篇:Pinia与Vuex对比