hexon
发布于 2025-10-10 / 8 阅读
0

五、Vue路由(一)

Vue路由是一个功能丰富的插件,完全可以单独出书讲解,因为内容较多,我将分多篇文章分享。本篇是路由篇的第一篇,介绍路由的历史与概念,并创建一个项目,先以动态组件的方式来说明为什么需要路由。

程序开发中的路由分为前端路由和后端路由,后端路由概念的出现早于前端路由。在传统MVC架构的Web开发中,路由规则由后台设置,后端路由先通过用户请求的URL分发到具体处理程序中,然后返回一个新页面给用户。用户每次访问新页面都需要发送新的请求,其中还可能有网络延迟,用户体验极差。因此,出现了前端路由,其原理是不同路由对应的不同内容页面由前端处理,后端只需要返回动态数据,实现用户访问不同的URL在前台切换页面显示不同的内容。

如何理解?

  • 后端用过servlet就知道,什么请求转发,重定向,都是在后端把数据嵌入到HTML中返回给浏览器的,浏览器再整体解析返回的内容。

  • 前端它可能请求的是一个index.html和js文件,切换路由是由js在浏览器中操作DOM,当然数据还是通过js中请求后端接口拿的

要说页面整体准备好,包括结构、样式、数据,其实前端路由和后端路由时间无法断定哪个更快。

前端渲染又叫客户端渲染 CSR(Client-Side Rendering)。

后端渲染又叫服务端渲染 SSR(Server-Side Rendering)。

现在前端也会结合 SSR,来弥补一些不足。

Web开发中的路由演进是从后端路由走向前端路由的历程。早期基于MVC的后端路由,通过服务器渲染(SSR)完整页面,每次跳转都导致整页刷新,体验较差。为追求更流畅的用户体验,出现了前端路由和SPA架构,其核心是客户端渲染(CSR):服务器仅提供初始HTML壳与JS资源,由浏览器中的JS通过操作DOM和调用API来动态更新视图,实现了无刷新导航。

值得注意的是,CSR与SSR在性能上各有优劣,CSR胜在后续交互体验,而SSR首屏加载更快且利于SEO,二者并无绝对的快慢之分。因此,现代前端框架(如Next.js/Nuxt.js)普遍采用SSR与CSR结合的同构渲染方案,旨在兼顾首屏性能与交互体验。

本章内容包括路由的概念与核心功能、动态组件加载、配置简单路由、路由链接高亮显示、嵌套路由、动态路由传参、路由参数映射、命名路由切换、命名视图渲染、编程式路由导航、路由过滤筛选、路由过渡动画效果、路由滚动行为、路由的异步懒加载、缓存路由组件、路由守卫、动态添加与删除路由。

路由的核心概念与功能

本节主要介绍路由的概念与后续要学习的路由的核心功能。

路由概念的提出

路由是前端应用中的核心内容。如果想要操作路由,就需要明白它的概念和目标是什么。

随着页面逐步增多,功能不断增强,单纯地利用组件切换页面已经无法满足应用程序的开发、扩展和维护需求。如果通过路由来实现,就可以轻松地解决这一难题。通过路由导航,用户就可以管理应用中不断增多的模块内容,并且可以根据业务的需求,在各个不同的模块之间进行自由切换操作。它就像公交、地铁的控制中心,通过该控制中心,可以将班车发送到不同的目地的,如下图所示。

学习路由首先需要掌握几个核心概念,主要包括静态路由表、分配地址、统一入口、寻找地址及地址过滤。光听这几个名词,你可能会有些困惑,或者理解不了路由到底是什么,下面我们就结合生活中的案例进行说明,可以思考一下在安装网络路由器时的操作。

对于大部分家庭来说,家中的路由器为台式计算机、智能手机、平板电脑、笔记本电脑甚至电视机等设备提供上网功能。为了不被他人通过非法的方式连接路由、占用网络带宽资源,我们往往会在路由器设备的管理后台中进行一些配置工作,如限制IP(Internet Protocol,互联网协议)访问、设定MAC(Media Access Control Address,物理地址)访问等,这就与上面提到的静态路由表和分配地址相似。假设我们在路由器设备的管理后台中为其中几台设备设置了固定的IP地址和MAC地址,让它们可以利用当前路由器进行网络访问,那么其他没有被设置过IP地址和MAC地址的设备还能访问网络吗?

上面描述的对应关系其实就是一张二维关系数据表。这张表一般是静态的,具体地说,这张表是固定分配好的,不能随意变化,因此我们将它将为静态路由表。假设此时管理后台已经分配好了各个地址,172.16.0.0对应的是台式计算机,172.16.1.0对应的是平板电脑,172.16.2.0对应的是智能手机,172.16.3.0对应的是笔记本电脑,172.16.4.0对应的是电视机,那么请思考,当设备在进行网络访问时,第1步做的是什么工作呢?当然是寻找地址!也就是查看当前设备的IP地址或者MAC地址是否已经存在于设置好的静态路由表中,如果已经存在,就可以进行网络访问;如果没有存在,就没有访问网络资源的权限。

这里是一个简化模型,实际上有点不严格,可以理解成为MAC地址设定了一个静态IP。

由此看来,所有的操作都经过了一个入口对象,这个入口对象就是路由器。如果由一个统一的对象进行管理,后续的操作就会变得更加便利。那隔壁邻居家的台式计算式是否可以通过你家的路由器进行网络访问呢?答案是:不可以。这是因为你家的路由器中没有设置邻居家台式计算机的地址,它就在路由器中被轻松过滤掉了。如果邻居家也申请了宽带,安装了路由器,他的台式计算机不通过你家的路由器地址进行联网操作,这也就意味着入口不同。邻居家的台式可以通过自己家的入口进行联网操作。也就是说,没有经过统一入口,也就与你家的网络没有任何关系。这里我们将访问来源、IP地址、MAC地址、对应设备通过表格和图片的方式进行展示,见下图。

路由信息:

接下来,对于路由功能的实现原理可以大致做一个剖析。所谓路由,其实就是为各个场景的切换提供一种方式,并且可以其导航历史。每个场景都像一个图层,可以被推送至其他场景的任意位置。当然,如果场景被删除,就无法实现该操作了。我们尝试以一种容易理解的方式进行解释,即将路由理解成翻书操作。所有的页就是我们配置的路由模块,将书翻至某一页,也就是其中某一个模块内容,翻至其他页,就是另一个模块内容。如果需要则可以将书回到刚才那一页,前提是不能把那一页撕掉。

简单理解就是记录了一个路径与组件的映射表,还会记录你访问的历史以实现能前进/后退。

Vue作为前端主流的框架之一也包含了路由功能,并且路由在Vue中扮演着 "项目开发基础与核心" 的重要角色,在Vue技术体系中是不可或缺的。Vue的路由功能主要由Vue官方插件VueRouter提供,VueRouter中包含了众多强大的路由操作功能。

路由的核心功能

想要在项目中使用路由模块,首先需要在Vue主体项目中安装与配置VueRouter路由插件,构建出基础的路由切换与路由渲染,然后在此基础上不断扩展与强化路由功能。后续会针对路由的核心功能介绍下面列表的一些要点。

  • 动态组件加载

  • 配置简单路由

  • 路由链接高亮显示

  • 嵌套路由

  • 动态路由传参

  • 路由参数映射

  • 命名路由切换

  • 命名视图渲染

  • 编程式路由导航

  • 路由过滤筛选

  • 路由过渡动画效果

  • 路由滚动行为

  • 路由的异步懒加载

  • 缓存路由组件

  • 路由守卫

  • 动态添加与删除路由

动态组件加载

本节主要讲解动态组件加载的实现,以及路径别名与省略后缀的配置。

动态组件加载的实现

在正式学习Vue路由内容之前,我们需要先了解动态组件加载的操作内容。其实除了利用Vue的路由插件来实现页面切换,Vue本身还内置了动态组件,只是它的功能相对来说比较单一,这里我们将其作为路由的前置技术来进行讲解。

下面分4步来实现一个动态组件的加载。

(1)复用vite创建一个路由项目,这里将项目的名称设置为 "vue3-book-router"。在cmd终端中执行下方命令:

npm create vite@latest vue3-book-router -- --template vue

补充:

特性

npm init vue@latest

npm create vite@latest

背后的创建工具

Vue 官方自己的创建工具:create-vue

Vite 官方的创建工具:create-vite

创建的项目类型

专门且仅用于创建 Vue 项目

可用于创建多种前端项目(Vue, React, Svelte, Vanilla等)

交互体验

交互式命令行问卷

通过 --template

参数指定模板

功能侧重

更“Vue生态原生”,提供更多Vue相关的可选配置

更“通用和快速”,专注于提供构建工具(Vite)基础

具体的操作如下:

(2)为了后续项目的页面优化,这里可以考虑引入bootstrap的样式内容。只需在项目根目录的主页面 "index.html" 中引入bootstrap的CDN地址即可,这里引入的版本为5.1.3。bootstrap的CDN地址可以在其官方网站中查找指定版本的CDN资源。

index.html代码如下:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.7/css/bootstrap.min.css" rel="stylesheet">
    <title>vue-router</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

这里改了html的title

(3)将App.vue文件中的代码内容全部删除,并将components目录与其他模板文件及其引用一同删除。新建页面目录views,在该目录下新建Home.vue与Users.vue两个页面文件。

views/Home.vue文件代码如下。

<template>
  <div>首页</div>
</template>

views/Users.vue文件代码如下。

<template>
  <div>用户页</div>
</template>

此时,文件目录结构结构如下图所示。

(4)在App.vue根组件文件中尝试引入Home.vue与Users.vue两个页面文件,并在根组件文件中放置两个按钮。希望在点击按钮时,实现首页与用户页的切换操作。

在引入Home.vue与Users.vue两个页面文件以后,先声明一个响应式数据selectPage,用于存放被选中的页面,并且定义一个切换页面的方法changePage,该方法的目标就是将selectPage这个响应式数据的值修改成被选中的页面。当然,为了让页面默认显示首页,可以进行方法的初始化调用操作。

而在模板渲染部分,则可以放置两个按钮,这两个按钮的功能就是点击触发页面切换,切换的内容就是Home与Users这两个页面组件。

那么被选中的页面最终在哪里显示呢?可以复用Vue的内置组件Component进行占位渲染,它有一个属性is,其值代表渲染的内容,而现在这个值则为响应式数据selectPage的内容。

此时App.vue文件代码如下。

<script setup>
import Home from './views/Home.vue';     // 引入首页
import Users from './views/Users.vue';   // 引入用户页
import { markRaw, ref } from 'vue'; 

const selectPage = ref(null);     // 设置选中组件的ref对象

// 定义切换页面的方法
const changePage = page => {
  // 注意:通过markRaw函数指定不对page组件本身进行响应式处理
  selectPage.value = markRaw(page)
}

// 初始化默认页面为首页
changePage(Home)

</script>

<template>
  <div class="container">
    <button class="btn btn-secondary" @click="changePage(Home)">
      切换到首页
    </button>
    &nbsp;
    <button class="btn btn-secondary" @click="changePage(Users)">
      切换到用户页
    </button>
    <hr>
    <!-- 利用动态组件加载,动态显示用户切换的页面内容 -->
     <component :is="selectPage"></component>
  </div>
</template>

这样,通过 "npm run dev" 命令运行项目进行测试。就可以看到,默认情况下页面显示了首页的内容,并且可以点击按钮进行切换。这个代码在上一章节中,讲动态组件其实已经介绍过了,这里只是做一个回顾。

到目前为止,利用动态组件加载的方式可以实现页面之间的切换,从实现效果上来说是没有任何问题的,但随着项目功能的增加,页面数量将急剧增加,动态组件加载方式会存在一定的局限。比如,对于两个页面之间的数据的传递、设置嵌套页面等常见操作,利用动态组件加载是容易处理的。也正是因为这些局限,才需要利用VueRouter插件来实现更好的页面切换和维护操作。

下面是一些其他的对比:

特性

动态组件切换

Vue Router

URL 变化

❌ 无

✅ 有,与视图同步

浏览器历史

❌ 无,无法前进后退

✅ 有,完整支持

深度链接

❌ 不支持

✅ 完美支持

导航守卫

❌ 需自行实现,复杂

✅ 内置,开箱即用

嵌套路由

❌ 实现复杂,难以维护

✅ 原生支持,结构清晰

代码组织

❌ 逻辑散乱,不易维护

✅ 集中配置,一目了然

SEO

❌ 极差,单URL无内容

✅ 更好,每个页面有独立URL

放弃动态组件加载而采用 Vue Router,本质上是为了拥抱 Web 的标准导航模型,并获取一个专业化工具带来的所有高级特性。

它不是为了解决“两个组件如何传参”这种微观问题,而是为了解决 “如何构建一个具有完整导航能力、可收藏、可分享、结构清晰且易于维护的现代 Web 应用” 这个宏观架构问题。

关键点在于路由应该结合浏览器的功能,而状态管理是状态管理。使用动态组件 (<component :is="currentComponent">) 来模拟页面切换,本质上是将导航行为变成了一个组件状态管理问题。这也不符合职责分离的设计理念,所以出现VueRouter是正常的,这就是背景。

路径别名与省略后缀的配置

在进行路由操作之前,其实可以先对引入Home.vue和Users.vue页面文件的操作进行一些优化,以便后续更多路由页面的引入。

这里我们可以修改项目的配置文件vite.config.js。首先引入Node和path模块中的resolve方法,然后修改配置项,添加resolve节点属性,设置alias别名与省略extensions后缀。配置好以后就可以优化App.vue文件中对于Home.vue和Users.vue页面文件的引入操作。

vite.config.js文件代码如下:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'    // 引入Node的path模块中的resolve方法
import { fileURLToPath } from 'url'

// 在 ESM 中获取 __dirname 的等效值
const __dirname = fileURLToPath(new URL('.', import.meta.url))

// https://vite.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    // 设置别名@,指向项目的src目录
    alias: [{ find: '@', replacement: resolve(__dirname, 'src') }],
    // 设置可以省略的后缀,包括.vue后缀等
    extensions: ['.js', '.ts', '.vue']
  }
})

注意:新版本的 Vite 和 Vue 更倾向于使用现代 ES 模块标准。

我创建的最新脚手架项目,package.json默认就指定了 "type": "module":

  • 这告诉 Node.js 使用 ES 模块 (ESM) 系统

  • 在 ESM 环境中,不能使用 require(),必须使用 import

  • __dirname 不是 ESM 的标准变量,不能直接使用

如果上面的配置不理解,就看官网或者问AI吧。

这里其实直接用 __dirname 也能识别,这估计是做了什么兼容操作,NodeJs这一块有历史原因错综复杂,先不纠结,也可能是Vite处理了,有时间再研究吧,反正知道能配置就行。但是为了符合规范,就按上面的方式配置即可。这玩意就是学习任何技术的一个痛点,新的工具往往要推一个新的用法,可是为了降低迁移成本又要向前兼容。但在学习阶段我们直接选最新的,最标准的做法就是了。

在修改配置文件以后,App.vue文件的页面引入部分就可以做一个简化。

<script setup>
import Home from '@/views/Home';     // 引入首页
import Users from '@/views/Users';   // 引入用户页
...
</script>

此时再次刷新页面,其结果并没有发生任何改变,应用程序同样能正常运行。