Vue Router 是 Vue3 官方路由管理器,负责页面跳转、路由参数传递、权限控制等核心功能,也是 CRM 系统 “多页面结构” 的基础。结合你之前关注的Pinia + onActivated,下面从「核心定位→基础用法→高级实战→与 Pinia/onActivated 协同」全维度讲解,贴合 CRM 开发场景。
一、核心定位(CRM 开发中为什么必须用?)
CRM 系统本质是 “多页面业务系统”(登录页、工作台、订单列表、订单详情、客户管理等),Vue Router 解决的核心问题:
- 页面跳转:比如登录后跳工作台、点击订单跳详情页;
- 路由传参:比如订单详情页通过路由获取
orderId; - 权限控制:比如未登录用户禁止访问工作台、不同角色访问不同页面;
- 页面缓存:配合
KeepAlive实现列表页缓存(结合onActivated刷新)。
二、基础用法(快速上手)
1. 安装与全局注册
bash
运行
# 安装(Vue3 对应 router@4) npm install vue-router@4typescript
运行
// src/router/index.ts(路由入口) import { createRouter, createWebHistory } from 'vue-router'; import Login from '@/views/Login.vue'; import Dashboard from '@/views/Dashboard.vue'; import OrderList from '@/views/OrderList.vue'; import OrderDetail from '@/views/OrderDetail.vue'; // 定义路由规则 const routes = [ { path: '/', // 默认路由 redirect: '/login' // 重定向到登录页 }, { path: '/login', name: 'Login', // 命名路由(用于 KeepAlive 缓存) component: Login }, { path: '/dashboard', name: 'Dashboard', component: Dashboard, meta: { requiresAuth: true } // 标记:需要登录才能访问 }, { path: '/orders', name: 'OrderList', component: OrderList, meta: { requiresAuth: true, keepAlive: true } // 需要登录 + 缓存组件 }, { path: '/orders/:id', // 动态路由参数(订单ID) name: 'OrderDetail', component: OrderDetail, meta: { requiresAuth: true }, props: true // 把路由参数映射为组件 props(可选) } ]; // 创建路由实例 const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), // HTML5 历史模式 routes }); // 全局注册(src/main.ts) import { createApp } from 'vue'; import { createPinia } from 'pinia'; import App from './App.vue'; import router from './router'; const app = createApp(App); app.use(createPinia()); app.use(router); app.mount('#app');2. 路由出口(App.vue)
vue
<template> <!-- 路由出口:渲染匹配的组件 --> <router-view v-slot="{ Component }"> <!-- 缓存指定组件(结合 keepAlive 标记) --> <keep-alive :include="['OrderList', 'Dashboard']"> <component :is="Component" /> </keep-alive> </router-view> </template>3. 组件中使用路由(跳转 / 传参)
(1)基础跳转(比如登录页跳工作台)
vue
<script setup lang="ts"> import { useRouter } from 'vue-router'; import { useUserStore } from '@/stores/user'; const router = useRouter(); const userStore = useUserStore(); const handleLogin = async () => { await userStore.login(loginForm.value); // 跳转到工作台 router.push('/dashboard'); // 或用命名路由(推荐,避免硬编码路径) // router.push({ name: 'Dashboard' }); }; </script>(2)动态路由传参(列表页跳详情页)
vue
<!-- OrderList.vue --> <script setup lang="ts"> import { useRouter } from 'vue-router'; const router = useRouter(); // 点击订单跳详情页,传递 orderId const handleGoDetail = (orderId: string) => { // 方式1:路径传参 router.push(`/orders/${orderId}`); // 方式2:命名路由 + 参数(推荐) // router.push({ name: 'OrderDetail', params: { id: orderId } }); }; </script>(3)详情页接收参数
vue
<!-- OrderDetail.vue --> <script setup lang="ts"> import { useRoute } from 'vue-router'; import { onMounted } from 'vue'; import { useOrderStore } from '@/stores/order'; const route = useRoute(); const orderStore = useOrderStore(); // 方式1:直接读取路由参数 const orderId = route.params.id as string; // 方式2:通过 props 接收(路由配置中开启 props: true) // const props = defineProps<{ id: string }>(); // const orderId = props.id; // 加载订单详情 onMounted(() => { orderStore.fetchOrderDetail(orderId); }); </script>三、高级实战(CRM 核心需求)
1. 路由守卫(权限控制)
CRM 最核心的路由需求是 “权限控制”(未登录禁止访问、不同角色访问不同页面),通过「全局前置守卫」实现:
typescript
运行
// src/router/index.ts import { useUserStore } from '@/stores/user'; // 全局前置守卫:跳转前验证权限 router.beforeEach((to, from, next) => { const userStore = useUserStore(); // 判断目标页面是否需要登录 if (to.meta.requiresAuth && !userStore.isLogin) { // 未登录:跳登录页,并记录目标页面(登录后跳转回去) next({ path: '/login', query: { redirect: to.fullPath } }); } else { // 已登录:判断角色权限(比如 admin 才能访问设置页) if (to.path === '/settings' && userStore.userInfo.role !== 'admin') { next('/dashboard'); // 无权限:跳工作台 } else { next(); // 放行 } } }); // 登录页跳转回原目标页面(Login.vue) const handleLogin = async () => { await userStore.login(loginForm.value); // 获取跳转前的目标页面,默认跳工作台 const redirect = route.query.redirect as string || '/dashboard'; router.push(redirect); };2. 与 Pinia + onActivated 协同(CRM 核心组合)
这是你最关心的点,结合三者实现 “缓存 + 刷新 + 数据一致”:
vue
<!-- OrderList.vue(订单列表页) --> <script setup lang="ts"> import { ref, onMounted, onActivated } from 'vue'; import { useRoute } from 'vue-router'; import { useOrderStore } from '@/stores/order'; const route = useRoute(); const orderStore = useOrderStore(); const searchKey = ref(''); // 封装刷新逻辑 const fetchOrderList = async () => { // 结合路由参数(比如筛选条件)+ Pinia 请求数据 const params = { searchKey: searchKey.value, page: route.query.page || 1 // 路由传分页参数 }; await orderStore.fetchOrderList(params); }; // 第一次渲染:初始化 onMounted(() => { fetchOrderList(); }); // 核心:从详情页返回时,触发刷新(组件已缓存) onActivated(() => { fetchOrderList(); }); </script>3. 路由参数监听(筛选条件变化刷新)
CRM 列表页常通过路由参数传递筛选条件(比如?status=paid&page=1),监听参数变化刷新数据:
vue
<script setup lang="ts"> import { watch } from 'vue'; import { useRoute } from 'vue-router'; const route = useRoute(); // 监听路由参数变化(比如筛选条件、分页) watch( () => route.query, () => { fetchOrderList(); // 参数变化,重新请求数据 }, { immediate: true } // 初始化时执行一次 ); </script>四、核心避坑点(CRM 开发常错)
1. 动态路由参数变化,组件不刷新
比如从/orders/1跳/orders/2,详情页不重新加载数据:
vue
<!-- 解决方案:监听路由参数变化 --> watch( () => route.params.id, (newId) => { orderStore.fetchOrderDetail(newId); // 重新加载新订单数据 }, { immediate: true } );2. KeepAlive 缓存后,数据不刷新
解决方案:用onActivated替代onMounted做刷新逻辑(前面已讲)。
3. 路由守卫中使用 Pinia 注意点
路由守卫执行时,Pinia 可能还未初始化?→ 直接在守卫内调用useUserStore()即可(Pinia 支持延迟初始化)。
五、与 Pinia/onActivated 协同总结(核心逻辑)
表格
工具 | 核心职责 | 协同方式 |
Vue Router | 页面跳转、路由传参、权限控制 | 1. 动态路由传参给组件(如 orderId);2. 路由参数传递筛选条件;3. 守卫控制页面访问权限 |
Pinia | 全局数据存储、异步请求封装 | 1. 组件从 Pinia 读取数据;2. Action 封装请求逻辑;3. 权限判断依赖 Pinia 的用户状态 |
onActivated | 缓存组件激活时刷新数据 | 1. 返回列表页时触发刷新;2. 刷新逻辑同步路由参数和 Pinia 数据 |
六、最终总结(CRM 开发最佳实践)
- 核心组合:
Vue Router(页面跳转/权限) + Pinia(全局数据) + onActivated(缓存刷新)是 CRM 开发的 “铁三角”,覆盖 100% 核心场景; - 路由设计原则:
- 按业务模块拆分路由(
/orders//customers//dashboard); - 动态路由传核心参数(如订单 ID),查询参数传筛选 / 分页条件;
- 给需要缓存的页面加
keepAlive标记,配合onActivated刷新;
- 按业务模块拆分路由(
- 权限控制:通过全局前置守卫 + Pinia 的用户状态,实现 “登录验证 + 角色权限” 双层控制;
- Mitt 定位:仅在 “跨页面 / 跨框架通信、纯 UI 动作触发” 等极特殊场景补位,核心场景完全不用。
这个 “铁三角” 组合是 Vue3 开发 CRM 系统的最优解,既保证了代码的规范性,又兼顾了用户体验(缓存 + 按需刷新),完全贴合你 “少用冗余工具、聚焦核心逻辑” 的开发思路。