
网罗开发 (小红书、快手、视频号同名)
大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。
图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG
我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验 。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。
展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索"展菲",即可纵览我在各大平台的知识足迹。
📣 公众号"Swift社区",每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友"fzhanfei",与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!
文章目录
-
- 前言
- [为什么 Vue Router 最容易失控?](#为什么 Vue Router 最容易失控?)
-
- 路由文件不断膨胀
- [beforeEach 成了"万能垃圾桶"](#beforeEach 成了“万能垃圾桶”)
- 页面结构、权限、布局全部耦合
- 路由架构设计的核心原则
-
- 原则一:路由只负责"路径映射"
- 原则二:业务模块必须"路由模块化"
- [原则三:meta 是"声明",不是"执行"](#原则三:meta 是“声明”,不是“执行”)
- 推荐的路由目录结构(非常关键)
- 路由模块拆分示例
- 统一的路由守卫架构
- [layout + route 的解耦设计](#layout + route 的解耦设计)
- 权限设计的正确姿势
-
- [meta 只做声明](#meta 只做声明)
- 权限逻辑集中处理
- 路由与页面生命周期管理
- [关于 DeepLink / URL 的设计建议](#关于 DeepLink / URL 的设计建议)
- 总结
前言
如果你接手过中大型 Vue 项目,大概率都会遇到这样一个阶段:
路由文件 1000 行起步
嵌套路由一层套一层
权限、布局、登录判断全写在 beforeEach
新同学不敢动,老同学不想动
然后某一天,你发现自己也开始往路由里"随手加一段逻辑",心里还安慰自己一句:
"先这样吧,后面再重构。"
这篇文章就来系统聊一件事:
Vue Router 到底应该怎么"架构",而不是怎么"凑合着用"。
为什么 Vue Router 最容易失控?
先说结论一句话版:
路由一乱,基本不是 Router 的锅,而是"职责没拆清楚"。
我们先看看常见的"失控现场"。
路由文件不断膨胀
最典型的结构:
js
// router/index.js
const routes = [
{
path: '/',
component: Layout,
children: [
{
path: '/user',
component: User,
meta: { requiresAuth: true }
},
{
path: '/order',
component: Order,
meta: { requiresAuth: true, role: 'admin' }
}
]
}
]
一开始还好,后面慢慢变成:
- 业务模块全堆在一个文件
- meta 越写越多
- 嵌套层级越来越深
最后谁都不敢动。
beforeEach 成了"万能垃圾桶"
js
router.beforeEach((to, from, next) => {
if (!isLogin()) {
next('/login')
}
if (to.meta.role === 'admin' && !isAdmin()) {
next('/403')
}
if (to.meta.title) {
document.title = to.meta.title
}
// 再加点埋点、再加点统计......
})
问题不在代码本身,而在于:
所有"页面级逻辑"都往这里塞。
时间一长,这里就变成了不可维护区域。
页面结构、权限、布局全部耦合
最典型的问题是:
- 一个路由 = 一个页面 + 一个布局 + 一套权限规则
- 需求一变,全链路都要改
这在多人协作时,几乎是灾难。
路由架构设计的核心原则
在讲方案前,先给你 3 条非常重要的原则:
原则一:路由只负责"路径映射"
路由的职责是:URL → 页面组件
不是:
- 权限系统
- 业务逻辑
- 页面状态管理
原则二:业务模块必须"路由模块化"
不要再把所有路由写在一个文件里。
原则三:meta 是"声明",不是"执行"
meta 只用来描述页面特性,不直接写逻辑。
推荐的路由目录结构(非常关键)
这是一个在中大型项目中非常稳的结构:
txt
router/
├── index.ts
├── modules/
│ ├── user.ts
│ ├── order.ts
│ ├── dashboard.ts
│ └── auth.ts
├── guard/
│ ├── auth.ts
│ ├── permission.ts
│ └── title.ts
└── types.ts
核心思路
- modules:只管"有哪些路由"
- guard:只管"进入路由前做什么"
- index:做组装,不写业务
路由模块拆分示例
modules/user.ts
ts
import type { RouteRecordRaw } from 'vue-router'
export const userRoutes: RouteRecordRaw[] = [
{
path: '/user',
component: () => import('@/layouts/MainLayout.vue'),
children: [
{
path: '',
name: 'UserList',
component: () => import('@/views/user/index.vue'),
meta: {
requiresAuth: true,
title: '用户列表'
}
}
]
}
]
特点:
- 只描述路径、组件、meta
- 不写权限判断逻辑
统一的路由守卫架构
index.ts
ts
import { createRouter, createWebHistory } from 'vue-router'
import { userRoutes } from './modules/user'
import { setupRouterGuard } from './guard'
const router = createRouter({
history: createWebHistory(),
routes: [
...userRoutes
]
})
setupRouterGuard(router)
export default router
guard/auth.ts
ts
export function authGuard(to, from, next) {
if (to.meta.requiresAuth && !isLogin()) {
next('/login')
} else {
next()
}
}
guard/title.ts
ts
export function titleGuard(to) {
if (to.meta.title) {
document.title = to.meta.title
}
}
guard/index.ts
ts
export function setupRouterGuard(router) {
router.beforeEach(authGuard)
router.afterEach(titleGuard)
}
这样做的好处是:
- 每个 guard 单一职责
- 新需求只加 guard,不改旧逻辑
- beforeEach 不再是"垃圾场"
layout + route 的解耦设计
推荐用 layout 作为中间层:
txt
/views
/layouts
├── MainLayout.vue
├── EmptyLayout.vue
路由里只负责选 layout:
ts
{
path: '/login',
component: () => import('@/layouts/EmptyLayout.vue'),
children: [...]
}
好处:
- 页面不感知布局
- 布局可复用
- 权限和 UI 解耦
权限设计的正确姿势
meta 只做声明
ts
meta: {
requiresAuth: true,
roles: ['admin']
}
权限逻辑集中处理
ts
function permissionGuard(to, from, next) {
const { roles } = to.meta
if (roles && !hasRole(roles)) {
next('/403')
} else {
next()
}
}
不要在页面里写权限判断。
路由与页面生命周期管理
合理使用:
onBeforeRouteEnteronBeforeRouteLeave
而不是所有状态都放 Vuex / Pinia。
ts
onBeforeRouteLeave(() => {
clearTempState()
})
关于 DeepLink / URL 的设计建议
- URL 代表页面状态
- 参数必须可恢复
- 不依赖内存状态
ts
/query?id=123
这样页面才能:
- 刷新不丢状态
- 分享可复现
总结
如果你记住一件事就够了:
路由不是业务逻辑的承载体,而是应用的"骨架"。
一旦 Router 乱了:
- 新功能变慢
- Bug 变多
- 新人上手成本暴涨
而一个清晰的路由架构,带来的好处是长期的工程红利。