Vue Router 越写越乱,如何架构设计?


网罗开发 (小红书、快手、视频号同名)

大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验 。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员

👋 大家好,我是展菲!

📱 全网搜索"展菲",即可纵览我在各大平台的知识足迹。

📣 公众号"Swift社区",每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。

💬 微信端添加好友"fzhanfei",与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。

📅 最新动态:2025 年 3 月 17 日

快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!

文章目录

前言

如果你接手过中大型 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()
  }
}

不要在页面里写权限判断。

路由与页面生命周期管理

合理使用:

  • onBeforeRouteEnter
  • onBeforeRouteLeave

而不是所有状态都放 Vuex / Pinia。

ts 复制代码
onBeforeRouteLeave(() => {
  clearTempState()
})
  • URL 代表页面状态
  • 参数必须可恢复
  • 不依赖内存状态
ts 复制代码
/query?id=123

这样页面才能:

  • 刷新不丢状态
  • 分享可复现

总结

如果你记住一件事就够了:

路由不是业务逻辑的承载体,而是应用的"骨架"。

一旦 Router 乱了:

  • 新功能变慢
  • Bug 变多
  • 新人上手成本暴涨

而一个清晰的路由架构,带来的好处是长期的工程红利

相关推荐
白兰地空瓶2 小时前
JavaScript 列表转树(List to Tree)详解:前端面试中如何从递归 O(n²) 优化到一次遍历 O(n)
javascript·算法·面试
大布布将军2 小时前
⚡️ 后端工程师的护甲:TypeScript 进阶与数据建模
前端·javascript·程序人生·typescript·前端框架·node.js·改行学it
chilavert3182 小时前
技术演进中的开发沉思-260 Ajax:核心动画
开发语言·javascript·ajax
程序员小易3 小时前
前端轮子(1)--前端部署后-判断页面是否为最新
前端·vue.js·node.js
xiaoxue..3 小时前
列表转树结构:从扁平列表到层级森林
前端·javascript·算法·面试
小oo呆3 小时前
【自然语言处理与大模型】LangChainV1.0入门指南:核心组件Agent
前端·javascript·easyui
BD_Marathon3 小时前
关于JS和TS选择的问题
开发语言·javascript·ecmascript
Hao_Harrision3 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨ | DrawingApp(画板组件)
前端·react.js·typescript·tailwindcss·vite7
dly_blog3 小时前
Vite 原理与 Vue 项目实践
前端·javascript·vue.js