Vue Router 实战规范:path/name/meta 配置 + 动态 / 嵌套路由,统一团队标准|状态管理与路由规范篇

【Vue Router】+ 中后台前端实战:从path/name/meta配置到动态嵌套路由,掌握统一可落地的路由规范,避开权限与刷新丢失高频坑!

📑 文章目录

  • 一、开篇
  • [二、path、name、meta 该咋用?](#二、path、name、meta 该咋用?)
    • [2.1 三者分别是什么](#2.1 三者分别是什么)
    • [2.2 path:URL 的门面](#2.2 path:URL 的门面)
    • [2.3 name:编程式跳转的「身份证」](#2.3 name:编程式跳转的「身份证」)
    • [2.4 meta:业务信息的「行李舱」](#2.4 meta:业务信息的「行李舱」)
  • 三、动态路由:权限与按需加载
    • [3.1 什么是动态路由](#3.1 什么是动态路由)
    • [3.2 实现思路](#3.2 实现思路)
    • [3.3 登录后加载动态路由](#3.3 登录后加载动态路由)
    • [3.4 动态路由常见坑](#3.4 动态路由常见坑)
  • 四、嵌套路由:多级页面结构
    • [4.1 什么是嵌套路由](#4.1 什么是嵌套路由)
    • [4.2 配置示例](#4.2 配置示例)
    • [4.3 父组件里放 router-view](#4.3 父组件里放 router-view)
    • [4.4 redirect 的常见用法](#4.4 redirect 的常见用法)
  • 五、统一规范总结
    • [5.1 路由配置规范](#5.1 路由配置规范)
    • [5.2 完整示例:一个规范的路由文件](#5.2 完整示例:一个规范的路由文件)
    • [5.3 和状态管理的关系](#5.3 和状态管理的关系)
  • 六、结语
  • [🔍 系列模块导航](#🔍 系列模块导航)

同学们好,我是 Eugene(尤金),一名多年中后台前端开发工程师。

(Eugene 发音 /juːˈdʒiːn/,大家怎么顺口怎么叫就好)

很多前端开发者都会遇到一个瓶颈:

代码能跑,但不够规范;功能能实现,但维护起来特别痛苦;一个人写没问题,一到团队协作就各种混乱、踩坑、返工。

想写出干净、优雅、可维护 的专业代码,靠的不是天赋,而是体系化的规范 + 真实实战经验

这一系列《前端规范实战》,我会用大白话 + 真实业务场景,不讲玄学、不堆理论,只分享能直接落地的规范、标准与避坑指南。

帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。


一、开篇

Vue Router 是每个 Vue 项目都会用到的路由方案,很多人虽然能写,但经常出现:

  • pathname 混用,跳转方式不统一
  • meta 只随手加字段,没有约定
  • 动态路由和权限混在一起,结构乱
  • 嵌套路由 childrenredirect 写不明白

本文从「日常怎么选、为什么这么选、容易踩的坑」出发,给出一套可落地的 Vue Router 使用规范,适合:

  • 会写 JS,但对路由概念不够清晰的同学
  • 刚开始接触 Vue 的初学者
  • 有经验、想巩固基础、统一团队习惯的前端工程师

不追求底层原理,目标是:看完就能照着用,减少踩坑。

[⬆ 返回目录](#⬆ 返回目录)

二、path、name、meta 该咋用?

2.1 三者分别是什么

属性 作用 是否必填
path URL 路径,浏览器地址栏显示的 必填(根路由可为 /
name 路由名称,用于编程式跳转和组件复用 建议必填
meta 自定义元信息,存面包屑、权限、标题等 按需使用

简单理解:

  • path :给人看的 URL,/user/profile
  • name :给代码用的标识,'UserProfile'
  • meta :给业务用的配置,如 { title: '个人中心', requireAuth: true }

[⬆ 返回目录](#⬆ 返回目录)

2.2 path:URL 的门面

规范建议:

  1. 用 kebab-case(短横线):/user-order-list,不要 /userOrderList
  2. 层级清晰:/user/profile/user/settings
  3. 动态参数用冒号:/user/:id

示例:

js 复制代码
// router/index.js
const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue'),
  },
  {
    path: '/user/:id',
    name: 'UserDetail',
    component: () => import('@/views/UserDetail.vue'),
  },
];

访问 /user/123 时,this.$route.params.id'123'

[⬆ 返回目录](#⬆ 返回目录)

2.3 name:编程式跳转的「身份证」

为什么推荐每个路由都加 name

  • name 跳转,不依赖 URL 变更,后期改 path 不影响跳转
  • 可配合 <keep-alive> 做缓存
  • 便于在守卫、面包屑里做逻辑判断

规范:用 PascalCase,语义清晰。

js 复制代码
// 推荐:语义清晰
{ name: 'UserProfile', path: '/user/profile', ... }
{ name: 'OrderDetail', path: '/order/:id', ... }

// 不推荐:过于简单
{ name: 'user', ... }
{ name: 'detail', ... }

跳转对比:

js 复制代码
// 用 path 跳转(path 改了就失效)
this.$router.push('/user/profile');

// 用 name 跳转(推荐)
this.$router.push({ name: 'UserProfile' });

// 带参数
this.$router.push({
  name: 'OrderDetail',
  params: { id: '123' },   // 对应 path 里的 :id
  query: { tab: 'shipping' }, // ?tab=shipping
});

踩坑提醒:params 和 query 的区别

  • params :对应 path 中的 :id,不会出现在 URL 的 query 里
  • query :会变成 ?key=value
js 复制代码
// 跳转
this.$router.push({
  name: 'UserDetail',
  params: { id: '123' },
  query: { from: 'home' },
});

// 实际 URL:/user/123?from=home
// this.$route.params.id  === '123'
// this.$route.query.from === 'home'

[⬆ 返回目录](#⬆ 返回目录)

2.4 meta:业务信息的「行李舱」

meta 用来放与路由相关的业务配置,常见用途:

用途 示例
页面标题 meta: { title: '个人中心' }
是否需要登录 meta: { requireAuth: true }
面包屑 meta: { breadcrumb: ['首页', '用户'] }
缓存 meta: { keepAlive: true }
权限码 meta: { permission: 'user:edit' }

规范建议:字段命名统一,团队共用一套约定。

js 复制代码
const routes = [
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('@/views/Dashboard.vue'),
    meta: {
      title: '控制台',
      requireAuth: true,
      keepAlive: true,
    },
  },
  {
    path: '/user/edit/:id',
    name: 'UserEdit',
    component: () => import('@/views/UserEdit.vue'),
    meta: {
      title: '编辑用户',
      requireAuth: true,
      permission: 'user:edit',
    },
  },
];

在路由守卫里使用:

js 复制代码
router.beforeEach((to, from, next) => {
  if (to.meta.requireAuth && !store.state.user.token) {
    next({ name: 'Login' });
  } else {
    next();
  }
});

在布局里设置标题:

js 复制代码
// 在 layout 或 App.vue 的 watch 里
watch(
  () => route.meta.title,
  (title) => {
    document.title = title || '默认标题';
  },
  { immediate: true }
);

[⬆ 返回目录](#⬆ 返回目录)

三、动态路由:权限与按需加载

3.1 什么是动态路由

动态路由 = 路由在运行时通过 router.addRoute() 动态添加,而不是一开始全部写死在配置里。

常见场景:根据用户角色/权限加载不同菜单和页面。

[⬆ 返回目录](#⬆ 返回目录)

3.2 实现思路

  1. 写一套「基础路由」:登录、404 等,不依赖权限
  2. 用户登录后,拿菜单/权限数据
  3. addRoute 把对应路由逐个加入
  4. 再跳转到目标页
js 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Layout from '@/layouts/MainLayout.vue';

// 1. 基础路由(所有人都能访问)
const constantRoutes = [
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login.vue'),
    meta: { title: '登录', noAuth: true },
  },
  {
    path: '/404',
    name: 'NotFound',
    component: () => import('@/views/404.vue'),
    meta: { title: '页面不存在', noAuth: true },
  },
];

// 2. 动态路由配置(根据权限筛选后 addRoute)
const asyncRouteMap = [
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('@/views/Dashboard.vue'),
    meta: { title: '控制台', icon: 'dashboard' },
  },
  {
    path: '/user',
    name: 'User',
    component: Layout,
    meta: { title: '用户管理', icon: 'user' },
    children: [
      {
        path: 'list',
        name: 'UserList',
        component: () => import('@/views/user/List.vue'),
        meta: { title: '用户列表' },
      },
    ],
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes,
});

export { constantRoutes, asyncRouteMap };
export default router;

[⬆ 返回目录](#⬆ 返回目录)

3.3 登录后加载动态路由

js 复制代码
// store/modules/user.js 或 登录逻辑处
import router, { asyncRouteMap } from '@/router';

// 模拟:根据权限过滤
function filterAsyncRoutes(routes, permissionList) {
  const res = [];
  routes.forEach((route) => {
    const tmp = { ...route };
    if (tmp.children) {
      tmp.children = filterAsyncRoutes(tmp.children, permissionList);
    }
    // 这里简化:实际可按 permission、role 等过滤
    res.push(tmp);
  });
  return res;
}

export async function login(account, password) {
  const res = await api.login(account, password);
  const { token, permissions } = res.data;

  // 存 token
  localStorage.setItem('token', token);

  // 按权限过滤路由
  const accessRoutes = filterAsyncRoutes(asyncRouteMap, permissions);

  // 动态添加
  accessRoutes.forEach((route) => {
    router.addRoute(route);
  });

  // 404 放最后
  router.addRoute({
    path: '/:pathMatch(.*)*',
    redirect: '/404',
  });
}

[⬆ 返回目录](#⬆ 返回目录)

3.4 动态路由常见坑

坑 1:刚 addRoute 就 push,可能匹配不到

js 复制代码
// 错误示例
router.addRoute(route);
router.push({ name: 'Dashboard' }); // 可能失败

addRoute 是同步的,但最好等下一次 tick 再跳转:

js 复制代码
router.addRoute(route);
await nextTick();
router.push({ name: 'Dashboard' });

坑 2:刷新后动态路由丢失

因为 addRoute 只存在内存中,刷新后路由会重置。正确做法:

  1. 登录成功后把「可访问路由」或权限存到 localStorage / sessionStorage
  2. router 初始化或 App.vueonMounted 里,先恢复 token,再根据存储的数据重新 addRoute
js 复制代码
// main.js 或 router 入口
if (localStorage.getItem('token')) {
  const accessRoutes = getStoredRoutes(); // 从 storage 读出
  accessRoutes.forEach((route) => router.addRoute(route));
}

坑 3:404 路由要最后加

404 的通配路由会匹配所有未定义 path,必须放在最后 add,否则后面的路由都会被当成 404。

[⬆ 返回目录](#⬆ 返回目录)

四、嵌套路由:多级页面结构

4.1 什么是嵌套路由

嵌套路由 = 父路由下挂子路由,形成多级页面结构。

典型结构:

复制代码
/dashboard          → 控制台(父)
/dashboard/overview → 控制台/概览(子)
/dashboard/analytics→ 控制台/分析(子)

实现方式:父路由用 children,父组件里用 <router-view> 渲染子路由。

[⬆ 返回目录](#⬆ 返回目录)

4.2 配置示例

js 复制代码
{
  path: '/dashboard',
  name: 'Dashboard',
  component: () => import('@/layouts/DashboardLayout.vue'), // 父布局
  redirect: '/dashboard/overview',  // 访问 /dashboard 时重定向到第一个子路由
  meta: { title: '控制台' },
  children: [
    {
      path: 'overview',
      name: 'DashboardOverview',
      component: () => import('@/views/dashboard/Overview.vue'),
      meta: { title: '概览' },
    },
    {
      path: 'analytics',
      name: 'DashboardAnalytics',
      component: () => import('@/views/dashboard/Analytics.vue'),
      meta: { title: '数据分析' },
    },
  ],
}

注意:

  • 子路由的 path 不要以 / 开头,否则会变成根路径
  • path: 'overview' 实际是 /dashboard/overview

[⬆ 返回目录](#⬆ 返回目录)

4.3 父组件里放 router-view

html 复制代码
<!-- layouts/DashboardLayout.vue -->
<template>
  <div class="dashboard-layout">
    <aside class="sidebar">
      <router-link :to="{ name: 'DashboardOverview' }">概览</router-link>
      <router-link :to="{ name: 'DashboardAnalytics' }">数据分析</router-link>
    </aside>
    <main class="content">
      <router-view />
    </main>
  </div>
</template>

子路由的组件会渲染在 <router-view /> 的位置。

[⬆ 返回目录](#⬆ 返回目录)

4.4 redirect 的常见用法

访问 /dashboard 时,若没有默认子路由,可能看到空白。建议加 redirect

js 复制代码
{
  path: '/dashboard',
  redirect: '/dashboard/overview',  // 或 { name: 'DashboardOverview' }
  children: [...]
}

多级嵌套时,每层都可以有 redirect,指向当前层第一个有意义的子路由。

[⬆ 返回目录](#⬆ 返回目录)

五、统一规范总结

5.1 路由配置规范

项目 规范
path kebab-case,层级清晰,动态参数用 :id
name PascalCase,每个路由都要有
meta 统一字段:titlerequireAuthkeepAlivepermission
跳转 优先用 name,需要参数时用 params + query
动态路由 基础路由 + 按权限 addRoute,404 放最后
嵌套路由 子 path 不加 /,父组件提供 <router-view>,合理使用 redirect

[⬆ 返回目录](#⬆ 返回目录)

5.2 完整示例:一个规范的路由文件

js 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import MainLayout from '@/layouts/MainLayout.vue';

const routes = [
  {
    path: '/',
    redirect: { name: 'Home' },
  },
  {
    path: '/home',
    name: 'Home',
    component: () => import('@/views/Home.vue'),
    meta: { title: '首页', noAuth: true },
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login.vue'),
    meta: { title: '登录', noAuth: true },
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: MainLayout,
    redirect: { name: 'DashboardOverview' },
    meta: { title: '控制台', requireAuth: true },
    children: [
      {
        path: 'overview',
        name: 'DashboardOverview',
        component: () => import('@/views/dashboard/Overview.vue'),
        meta: { title: '概览', keepAlive: true },
      },
      {
        path: 'user/:id',
        name: 'DashboardUserDetail',
        component: () => import('@/views/dashboard/UserDetail.vue'),
        meta: { title: '用户详情', requireAuth: true },
      },
    ],
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    redirect: '/404',
  },
  {
    path: '/404',
    name: 'NotFoundPage',
    component: () => import('@/views/404.vue'),
    meta: { title: '页面不存在' },
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

router.beforeEach((to, from, next) => {
  if (to.meta.requireAuth && !localStorage.getItem('token')) {
    next({ name: 'Login', query: { redirect: to.fullPath } });
  } else {
    document.title = to.meta.title || '我的应用';
    next();
  }
});

export default router;

[⬆ 返回目录](#⬆ 返回目录)

5.3 和状态管理的关系

  • 路由:管「当前在哪个页面」、URL、面包屑、权限控制
  • 状态管理(如 Pinia/Vuex):管「页面里的业务数据」

典型分工:

  • 路由守卫里做:是否登录、是否有权限、重定向
  • Store 里做:用户信息、列表数据、表单数据
js 复制代码
// 路由守卫里:只做校验和跳转
if (to.meta.requireAuth && !store.state.user.token) {
  next({ name: 'Login' });
  return;
}

// Store 里:业务数据
store.dispatch('user/fetchProfile');

不要把大量业务状态塞进 metameta 只放和路由本身相关的配置。

[⬆ 返回目录](#⬆ 返回目录)

六、结语

Vue Router 日常使用只要抓住几条主线:

  1. path / name / meta 各司其职,命名统一
  2. 动态路由:基础路由 + 登录后按权限 addRoute,刷新要能恢复
  3. 嵌套路由children + 父组件里的 <router-view>,配合 redirect
  4. 与 Store 分工:路由管「去哪」,Store 管「数据」

按这些规范写,可读性和可维护性都会好很多,也方便团队协作。

🔍 系列模块导航

📝 状态管理与路由规范

一、《Vue3 Pinia 状态管理规范:状态拆分、Actions 写法、持久化实战,避坑状态污染|状态管理与路由规范篇》
二、《Vue3 Pinia 状态管理规范:何时用 Pinia 何时用本地状态|状态管理与路由规范篇》

三、《Vue Router 实战规范:path/name/meta 配置 + 动态 / 嵌套路由,统一团队标准|状态管理与路由规范篇》
四、《Vue3 + Vue Router + Pinia 路由守卫规范:beforeEach 应做 / 不应做,避死循环、防重复请求|状态管理与路由规范篇》
五、《Vue keep-alive 实战避坑:include/exclude + 路由 meta 标记,中后台路由缓存精准可控|状态管理与路由规范篇》

👉 跟着系列慢慢学,把技术功底扎扎实实地打牢~

📚 系列总览

前端规范实战系列 」正在持续更新中,后续会整理一篇《前端规范实战系列全系列目录导航》,包含每篇文章简介 + 直达链接,方便大家按顺序、体系化学习。

更新中,敬请期待~

[⬆ 返回目录](#⬆ 返回目录)


技术成长,从来不是比谁写得快,而是比谁写得稳、规范、可维护

哪怕每次只吃透一条规范,长期下来,差距会非常明显。

后续我会持续更新前端规范、工程化、可维护代码相关实战干货,帮你告别面条代码、维护噩梦,在开发与面试中更有底气。

觉得有用欢迎 点赞 + 收藏 + 关注,不错过每一篇实战内容。

我是 Eugene,与你一起写规范、写优质代码,我们下篇干货见~

相关推荐
干啥啥不行,秃头第一名1 小时前
STL容器内部实现剖析
开发语言·c++·算法
2401_831824962 小时前
内存泄漏检测与防范
开发语言·c++·算法
小彭努力中2 小时前
194.Vue3 + OpenLayers 实战:动态位置 + 高度 + 角度,模拟卫星地面覆盖范围
前端·css·vue.js·openlayers·animate
颜正义2 小时前
作为前端你还不会 Playwright 进行单元测试吗?
前端·测试
孟陬2 小时前
国外技术周刊 #3:“最差程序员”带动高效团队、不写代码的创业导师如何毁掉创新…
前端·后端·设计模式
张一凡932 小时前
easy-model -- "小而美"的React状态管理方案
前端·javascript·react.js
前端Hardy2 小时前
纯 HTML/CSS/JS 实现的高颜值登录页,还会眨眼睛!少女心爆棚!
前端·javascript·vue.js
includei2 小时前
【JavaScript】华为机试_HJ20_密码验证合格程序
开发语言·javascript·华为
肖恭伟2 小时前
Cursor Superpowers 零基础开发 Qt 界面
开发语言·qt