搞懂 SPA 再学路由!Vue Router 从0到完善 + 嵌套路由一次性梳理

1 阅读12分钟

搞懂 SPA 再学路由!Vue Router 从0到完善 + 嵌套路由

说实话,回想刚学 Vue 路由的时候,整个人都是懵的。 为啥 Vue 非要整个  vue-router ? 以前写静态页面用 a 标签点一下就跳转了,啥配置都不用,为啥到了 Vue 项目就必须学路由?

相信很多小伙伴和我一样:路由配置会抄、代码会写,但本质一直没吃透,尤其是嵌套路由,一直搞不懂为啥要多套一层  children 。

所以这篇文章我按照自己从零理解、慢慢顿悟的顺序来分享,不讲死板概念、不搞老师讲课那套,纯纯是自己踩坑后的总结。

从「为什么要有路由」开始,到手把手迭代一版能直接上项目的完整路由,最后用最通俗的大白话讲明白「嵌套路由到底是个啥、到底有啥用」,看完你绝对彻底通透。

一、先搞懂根源:从网页的两种形态,弄懂「路由为什么必须学」

想要学明白路由,咱先别碰代码,先搞懂一个最核心的问题:前端路由到底是为了解决什么问题诞生的?

1. 老式网页 MPA:根本不需要路由

我们早年的普通网页、静态页、PHP 网页,都属于 MPA 多页面应用。

这种网页的逻辑特别简单粗暴:

- 每一个页面,都是一个独立的  html  文件

- 点一下导航,浏览器整页刷新、白屏、重新加载

跳转靠原生标签,浏览器自带能力,不用我们写任何逻辑说白了,这种模式就是:跳转 = 换一个全新网页。

这种模式下,完全不需要前端路由,因为浏览器已经帮我们把跳转干完了。

2. Vue 项目 SPA:逼出了「前端路由」

但我们现在写的 Vue 项目,全部都是 SPA 单页面应用。

最关键的核心:

Vue 项目从头到尾,只有唯一的一个 index.html。

你在项目里看到的很多个页面,根本不是一个个 html 文件,全部都是组件。

浏览器本来只会两种操作:

1. 打开新网页(整页刷新) 2. 原地不动

但 Vue 想要的效果是:地址栏变了、页面内容换了,但是网页不刷新、不白屏、不重载。

浏览器原生做不到这件事,所以就必须我们自己手动写一套规则——这套规则,就是vue-router

到这里就彻底明白了: 不是我们想学路由,是 SPA 单页面模式,强制我们必须用路由!没有路由,Vue 项目根本跑不起来页面跳转。

3. 捋清楚 Vue 页面的真实层级以及SPA原理(再也不乱了)

一开始迷糊,就是因为搞不清谁套谁、谁先执行。我用最通俗的顺序给大家捋一遍:

浏览器最外层:index.html(纯空壳,只有一个 #app 容器)
        ↓
main.js(项目真正入口,最先执行)
        ↓
挂载路由 + 挂载根组件 App.vueApp.vue(项目内部大壳子,唯一根组件)
        ↓
<router-view>(路由占位坑位)
        ↓
动态切换:Login.vue / Home.vue / 404.vue之类页面组件

总结大白话:

1. index.html 只是浏览器空壳,不执行任何逻辑

2. main.js 是真正的程序启动入口

3. App.vue 是 Vue 项目的内部根壳

4. 所有路由跳转,都在 App.vue 内部切换,不会跳出外层 index.html

这就是 SPA 无刷新跳转 的底层原理。

二、从零手写并完善一版路由

原理吃透,直接上手实战! 从最简骨架开始,逐版本叠加企业级功能,每一步都是项目刚需,最终产出一套「开箱即用、无 bug、带权限」的标准路由配置。

前置准备:

安装依赖:Vue3 专属路由版本为  vue-router@4 ,直接终端执行安装:npm install vue-router@4  

规范项目文件结构:坚决不要把路由逻辑写在  main.js  里!项目越大越乱! 统一拆分独立文件更规范:

src/router/index.js  // 所有路由配置统一管理

版本1:最简路由骨架(吃透路径匹配核心)

只保留最核心的「路径-组件」匹配逻辑,看懂这个就懂路由底层:

// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";

// 静态引入页面组件(基础写法)
import Login from "@/views/Login.vue";
import Home from "@/views/Home.vue";

// 路由规则数组:路径和页面一一绑定
const routes = [
  // 根路径重定向:打开根地址自动跳转到首页
  { path: "/", redirect: "/home" },
  // 登录页路由
  { path: "/login", component: Login },
  // 首页路由
  { path: "/home", component: Home },
];

// 创建路由实例
const router = createRouter({
  // history 模式:去除地址栏 # 号,企业项目首选
  history: createWebHistory(),
  // 绑定路由规则
  routes,
});//这两个参数必传

// 导出路由,供 main.js 全局挂载
export default router;

在创建路由时,我们还需要指定路由模式。vue-router 一共提供了三种:

-  createWebHistory :history 模式,网址干净清爽,这是目前最常用的模式,正式项目首选,但需要后端配合处理刷新 404。

-  createWebHashHistory :hash 模式,链接带 #,不用后端配置,刷新不会报错,适合内部工具。

-  createMemoryHistory :内存模式,不依赖地址栏,多用于小程序、SSR 等非浏览器环境,前端项目很少用。

版本2:新增 meta 元信息(权限、标题必备)

真实开发中,我们需要给页面打标签:是否需要登录、页面标题、权限标识等。  meta  就是路由的自定义元信息,是做权限控制、页面标题的核心:

const routes = [
  { path: "/", redirect: "/home" },
  { 
    path: "/login", 
    component: Login, 
    // 自定义页面属性
    meta: { 
      requiresAuth: false, // false:不需要登录即可访问
      title: "登录页面" 
    } 
  },
  { 
    path: "/home", 
    component: Home, 
    meta: { 
      requiresAuth: true, // true:需要登录权限
      title: "系统首页" 
    } 
  },
];

版本3:路由懒加载 + 404 兜底(性能+体验优化)

静态引入是纯新手写法! 项目页面多了之后,所有组件一次性加载,会导致首屏加载巨慢、卡顿。 统一用路由懒加载:访问哪个页面,就加载哪个页面的代码,大幅优化首屏性能。 同时新增 404 兜底页面,防止用户输错网址出现空白页:

import { createRouter, createWebHistory } from "vue-router";

// 懒加载写法:按需引入组件
const Login = () => import("@/views/Login.vue");
const Home = () => import("@/views/Home.vue");
const NotFound = () => import("@/views/NotFound.vue");

const routes = [
  { path: "/", redirect: "/home" },
  { path: "/login", component: Login, meta: { requiresAuth: false, title: "登录页面" } },
  { path: "/home", component: Home, meta: { requiresAuth: true, title: "系统首页" } },
  // 全局404兜底:所有不存在的路径,全部匹配404页面
  { path: "/:pathMatch(.*)*", component: NotFound, meta: { title: "页面走丢了" } },
];

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

export default router;

版本4:细节 bug 修复(上线必备优化)

两个项目高频 bug,提前根治:

1. 页面跳转后,滚动条不自动置顶,体验极差

2. 重复点击同一路由,控制台报红报错

优化后完整代码:

import { createRouter, createWebHistory } from "vue-router";

const Login = () => import("@/views/Login.vue");
const Home = () => import("@/views/Home.vue");
const NotFound = () => import("@/views/NotFound.vue");

const routes = [
  { path: "/", redirect: "/home" },
  { path: "/login", component: Login, meta: { requiresAuth: false, title: "登录页面" } },
  { path: "/home", component: Home, meta: { requiresAuth: true, title: "系统首页" } },
  { path: "/:pathMatch(.*)*", component: NotFound, meta: { title: "页面走丢了" } },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
  // 页面跳转自动回到顶部
  scrollBehavior() {
    return { top: 0, left: 0 };
  },
});

// 修复:重复点击同一路由控制台报红问题
const originalPush = router.push;
router.push = function push(location) {
  return originalPush.call(this, location).catch(() => {});
};

export default router;

版本5:最终完整版(路由守卫+权限控制)

这是可以直接上线的终极版本! 新增全局路由前置守卫,实现三大核心功能:

- 自动修改浏览器页面标题

- 未登录禁止访问首页等权限页面

- 已登录禁止重复进入登录页

完整版本:

// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";

// 路由懒加载引入所有页面
const Login = () => import("@/views/Login.vue");
const Home = () => import("@/views/Home.vue");
const NotFound = () => import("@/views/NotFound.vue");

// 路由规则配置
const routes = [
  { path: "/", redirect: "/home" },
  { path: "/login", component: Login, meta: { requiresAuth: false, title: "登录页面" } },
  { path: "/home", component: Home, meta: { requiresAuth: true, title: "系统首页" } },
  { path: "/:pathMatch(.*)*", component: NotFound, meta: { title: "页面走丢了" } },
];

// 创建路由实例
const router = createRouter({
  history: createWebHistory(),
  routes,
  // 跳转页面自动置顶
  scrollBehavior() {
    return { top: 0, left: 0 };
  },
});

// 修复重复导航报错
const originalPush = router.push;
router.push = function push(location) {
  return originalPush.call(this, location).catch(() => {});
};

// 全局前置路由守卫:页面跳转前触发
router.beforeEach((to, from) => {
  // 1. 自动设置页面标题
  document.title = to.meta.title || "后台管理系统";
  // 2. 获取本地登录令牌,判断登录状态
  const token = localStorage.getItem("token");

  // 需登录权限的页面:无token强制跳转登录页
  if (to.meta.requiresAuth) {
    if (!token) return "/login";
  } else {
    // 已登录状态,禁止再次进入登录页
    if (token && to.path === "/login") return "/home";
  }

  // 放行,允许页面跳转
  return true;
});

export default router;
最后一步:全局挂载路由

路由文件写完后,必须在  main.js  挂载才能全局生效,标准写法:

// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
// 引入路由文件
import router from './router'

// use(router) 全局注册路由
createApp(App).use(router).mount('#app')
补充:路由最常用的基本使用方法

路由配置好之后,在页面里最常用的就两件事:跳转页面 和 拿当前路由信息,日常开发基本就靠这几个。

1. 模板里跳转:router-link

相当于不刷新页面的 a 标签,直接用:

<router-link to="/home">首页</router-link>
<router-link to="/user">用户管理</router-link>

2. JS 逻辑里跳转(点击按钮常用)

//在  <script setup>  里通过  useRouter  实现跳转,最真实常用的写法:
  
<template>
  <button @click="goHome">跳转到首页</button>
</template>

<script setup>
import { useRouter } from 'vue-router'

// 获取路由实例
const router = useRouter()

// 点击跳转
const goHome = () => {
  router.push('/home')
}
</script>

3. 返回上一页

const goBack = () => {
  router.back()
}

4. 获取当前路由信息(路径、参数等)

<script setup>
import { useRoute } from 'vue-router'

const route = useRoute()

// 当前路径
console.log(route.path)
// 获取 ?id=123 这类参数
console.log(route.query.id)
</script>

5. 路由显示出口:router-view

所有路由页面都渲染在这个位置,必须写在  App.vue  或布局组件里:

<router-view /> 

三、嵌套路由梳理

前面写的都是一级普通路由,逻辑很简单:跳转即整个内部页面全部替换。 但我们真实开发的后台系统,几乎全部依赖嵌套路由!

1. 通俗理解嵌套路由

先看后台系统的通用布局:

- 左侧侧边栏、顶部导航栏:永久固定,不会变化

- 只有中间内容区域:点击菜单实时切换

这就是嵌套路由的专属场景!

大白话直接总结两者区别:

  • 普通一级路由:替换整个大页面(壳子+内容全换)
  • 嵌套路由: 外层大壳子固定不动,只替换壳子内部的小块内容

嵌套路由核心本质: 在一个父页面(布局壳子)里,再写一个 ( <router-view > )坑位,专门用来渲染子页面,实现局部无刷新切换。

2. 为什么必须用嵌套路由?

我全部写一级路由不行吗?

行是行,但会出现三个致命问题,项目完全无法使用:

1. 代码极度冗余:每个页面都要重复写侧边栏、顶部导航,重复代码满天飞

2. 页面体验极差:切换页面会刷新整个布局,出现闪屏、白屏

3. 状态全部重置:菜单折叠、用户信息、滚动位置等状态,切换页面全部清零

只要是「固定布局 + 动态内容」的页面,嵌套路由是刚需。

3. 嵌套路由实战写法

第一步:新建全局布局外壳组件  Layout.vue

这是所有子页面的父壳子,侧边栏、头部全部写在这里,永久固定: 顶部导航 / 侧边信息

    <!-- Layout.vue 固定结构 -->
<template>
  <div class="layout">
    <!-- 侧边栏 + 顶部导航 -->
    <div class="sidebar">...</div>
    <div class="header">...</div>

    <!-- 子页面渲染坑位,必须写! -->
    <router-view />
  </div>
</template>
第二步:配置 children 嵌套路由规则

父路由绑定布局壳子, children  数组存放所有子页面,结构超级清晰

// 引入布局组件 & 子页面组件
const Layout = () => import("@/views/Layout/Layout.vue");
const Login = () => import("@/views/Login.vue");
const User = () => import("@/views/User.vue"); // 用户管理子页面
const Order = () => import("@/views/Order.vue"); // 订单管理子页面
const NotFound = () => import("@/views/NotFound.vue");

const routes = [
  { path: "/", redirect: "/login" },
  { path: "/login", component: Login, meta: { requiresAuth: false, title: "登录页面" } },
  {
    // 父路由地址
    path: "/home",
    // 父路由绑定全局布局壳子
    component: Layout,
    meta: { requiresAuth: true, title: "系统首页" },
    // 嵌套子路由:所有内部切换的页面
    children: [
      // 子路由路径无需加 /
      { path: "user", component: User, meta: { title: "用户管理" } },
      { path: "order", component: Order, meta: { title: "订单管理" } },
    ],
  },
  { path: "/:pathMatch(.*)*", component: NotFound, meta: { title: "页面走丢了" } },
];    

小技巧:想让进入  /home  时默认显示某个子页面,可以加一行重定向:

    redirect: '/home/user'

这样用户一进后台,就自动显示用户管理页面,不会空白。

嵌套路由 3 个必记关键点

1. 父路由组件(Layout)内部必须写 ( <router-view >),子页面才知道渲染在哪里。

2. 子路由  path  不要以  /  开头,直接写  user 、 order ,会自动拼接父路由路径。

3. 子组件和普通页面写法完全一致,都是  .vue  文件,只需在路由里正确  import  引入即可。

大白话总结:一级路由管整个大页面,嵌套路由管页面里的小模块!

四、全文核心思路复盘

最后串联全文逻辑,彻底打通 Vue-Router 知识体系:

1. 先懂底层逻辑:区分 MPA 和 SPA 差异,明白 Vue 单页面无刷新跳转的特性,决定了项目必须依赖  vue-router,搞懂技术存在的意义,不再死记硬背。

2. 渐进式手写企业路由:从最简路由骨架起步,一步步叠加  meta  元信息、懒加载性能优化、404 兜底、滚动优化、报错容错,最后搭配全局路由守卫,实现权限控制、标题自动修改,迭代出一套完整、可直接上线的标准化路由配置,每一个功能都对应真实项目需求。

3. 吃透嵌套路由核心:区分普通路由和嵌套路由的使用场景,掌握「父布局壳子 + 子路由嵌套」的实战通用写法,解决传统一级路由的代码冗余、页面闪屏、状态重置问题,适配所有后台管理系统布局。

本篇 Vue Router 完整知识点梳理到此结束,下一篇实战梳理 Vue3 全局状态管理核心:Pinia