两种路由模式
Vue Router 使用两种主要的方式来监听 URL 的变化:一种是基于浏览器的
history API,另一种是基于 hash
变化。这两种方式分别对应 history 模式和 hash
模式。
不管是 hash 模式还是 history
模式,核心目的都是在不刷新整个页面的前提下,实现前端路由的切换(单页应用
SPA 的核心需求),只是监听 URL 变化的方式和 URL 表现形式不同。
history 模式
在 history 模式下,Vue Router 利用 HTML5 的
history API
来实现路由的切换。(pushState、replaceState 和
popstate 事件)
- 这些 API
允许开发者在不刷新页面的情况下,修改浏览器的历史记录栈,并且 URL
中不再包含
#符号 - Vue Router 会监听浏览器的
popstate事件(当用户点击浏览器的前进 / 后退按钮时触发),同时通过pushState/replaceState方法修改 URL 并渲染对应组件。 - 当用户导航到新的路由时,Vue Router 会使用
history.pushState或history.replaceState方法来更改浏览器的历史记录
因为它不包含 # 符号。所以 URL 更美观、对 SEO
更友好,但需要后端配置重定向,仅支持现代浏览器,适合生产环境的正式项目
若项目部署在常规的 Web 服务器(Nginx/Apache)上,优先选择
history 模式并配置后端重定向
需要使用 createWebHistory 来创建 history
模式的路由实例:
1 | // src/router/index.js |
hash 模式
hash 模式,它不会改变浏览器的
URL,但页面会重新加载。它是默认模式,兼容性好、而且无需后端配置,但 URL
带 #,适合对 URL
美观度要求不高、需要兼容老旧浏览器的项目。
hash 指的是 URL 中 # 符号后面的部分(比如
http://localhost:5173/#/home 中,#/home 就是
hash)。
- 浏览器在处理
hash时,不会将#及后面的内容发送给服务器,它只是浏览器端的一个标识。 - Vue Router 的
hash模式会监听浏览器的hashchange事件(当 URL 中的 hash 发生变化时触发),从而根据新的 hash 值渲染对应的组件。
貌似没看到过不支持 hash 模式的老东西,它貌似支持所有浏览器,因为
hashchange 事件是很早就支持的特性。
而且因为 hash 部分不会发送到服务器,所以即使直接刷新页面,服务器也不会处理 hash,不会出现 404 错误。
在创建路由实例时,默认就是 hash
模式,也可以显式指定:
1 | // src/router/index.js |
此时访问的 URL
形式是:http://localhost:5173/#/、http://localhost:5173/#/about。
其他的路由模式
Memory 模式
他会假设自己不在浏览器中,所以我们不考虑它是一种主流的浏览器路由模式
它的核心特点是路由的历史记录只存储在内存中,不与浏览器的 URL、历史记录栈交互,也不依赖浏览器环境。
Memory 模式完全 “脱离” 浏览器的 URL 和历史记录,路由的变化只在程序内存中发生,外部完全感知不到。
正因为它不依赖浏览器环境,所以主要用于非浏览器场景:
- Node.js 环境:比如在 Node 端执行路由相关的逻辑(如数据预取),此时没有浏览器的 URL 和 history API,Memory 模式是最佳选择。
- 服务端渲染(SSR):Vue 服务端渲染时,服务端没有浏览器环境,需要用 Memory 模式来处理路由,避免依赖浏览器的 API。
通过 createMemoryHistory() 创建 Memory
模式的路由实例
1 | // src/router/index.js |
Memory
模式不会自动触发初始的路由导航(比如默认跳转到
/),所以需要在挂载应用后手动调用
router.push() 来指定初始路由:
1 | // main.js |
在浏览器中使用时,用户无法通过后退 / 前进按钮切换路由,因为 Memory 模式没有修改浏览器的历史记录栈。
即使在浏览器中,路由切换时地址栏的 URL 也不会发生任何变化(始终是页面初始的 URL)。
官方明确不建议在普通的浏览器端应用中使用 Memory 模式,因为它会丢失用户熟悉的 URL 导航、历史记录操作等体验,只有非浏览器场景才需要用。
命名视图
回顾视图
在 Vue Router 中,视图(View) 本质上是路由渲染组件的占位容器
视图就是一个坑位,路由会根据匹配的规则,把对应的组件渲染到这个 坑位 里(byd厕所雅间)
在 Vue 组件模板中,基础视图对应的是 <router-view>
标签,这是 Vue Router 提供的内置组件,也是最核心的视图载体。
当你配置了路由规则后,访问对应的路径时,Vue Router 会自动将路由规则中
component 对应的组件渲染到 <router-view>
的位置。
一个典型的单视图场景
1 | <!-- App.vue(根组件) --> |
对应的路由配置:
1 | // src/router/index.js |
为什么说是坑位,是因为<router-view>
本身不渲染任何内容,只是作为组件的挂载点。而且视图的内容由路由规则的
component(或 components)属性决定。
嵌套路由就是可以在组件内部再次使用
<router-view>实现
命名视图
命名视图是解决「同一页面渲染多个组件」的问题
基础视图(默认的
<router-view>)只能在页面中渲染一个组件,但实际开发中,你可能会遇到需要在同一级路由下,同时渲染多个组件到不同位置的场景,比如:
- 页面分为头部、侧边栏、主内容区三个部分,且这三个部分的内容都由路由控制。
此时用基础视图无法实现,因为一个 <router-view>
只能放一个组件,这时候就需要命名视图。
命名视图就是给 <router-view> 标签添加一个
name
属性,使其成为一个具名的占位容器。同时,在路由规则中使用
components(注意是复数,它有个s)属性,为不同名称的视图指定对应的组件。
1 | <!-- App.vue --> |
这里定义了三个视图:
- 命名视图
header:渲染头部组件 - 命名视图
sidebar:渲染侧边栏组件 - 默认视图
default:渲染主内容组件(没有name属性时,默认名称是default)
然后在路由规则中配置 components(复数)
1 | // src/router/index.js |
然后创建对应的组件就行了
嵌套命名视图
和普通视图一样,命名视图也可以嵌套,这个场景多用于广告上,即在一个命名视图渲染的组件内部,再定义新的命名视图(或默认视图),实现更复杂的布局。
1 | /settings/emails /settings/profile |
Nav只是一个常规组件。UserSettings是一个视图组件。UserEmailsSubscriptions、UserProfile、UserProfilePreview是嵌套的视图组件。
示例:在 HomeView 组件中嵌套命名视图
1 | <!-- src/views/HomeView.vue --> |
对应的路由配置(添加子路由):
1 | const routes = [ |
重定向
重定向
重定向是指当用户访问某个路径(如 /home)时,Vue Router
会自动将其导航到另一个指定的路径(如 /),URL
地址栏会显示目标路径的地址。简单来说,重定向是 “用户访问
A,系统强制跳转到 B”,且地址栏会发生变化。
Vue Router 的重定向完全通过 routes
配置实现,主要有三种写法
静态字符串重定向
直接指定重定向的目标路径字符串,一般就是固定的跳转场景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{ path: '/', component: Home, name: 'home' }, // 根路径
{ path: '/about', component: About },
// 访问 /home 时,重定向到根路径 /
{ path: '/home', redirect: '/' }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router当用户输入
http://localhost:5173/home时,地址栏会立刻变成http://localhost:5173/,并渲染 Home 组件。命名路由重定向
通过路由的
name属性指定重定向目标,避免硬编码路径1
2
3
4const routes = [
{ path: '/', component: Home, name: 'home' }, // 给路由命名为 home
{ path: '/home', redirect: { name: 'home' } } // 重定向到命名路由 home
]如果后续修改了根路径的
path(比如改成/index),只需修改路由的path属性,重定向配置无需改动动态函数重定向
这个支持自定义的逻辑,通过一个函数返回重定向目标,函数接收目标路由对象
to作为参数(包含当前访问的路径、参数、查询参数等信息),可以根据这些信息动态决定跳转方向,适用于复杂的业务场景。1
2
3
4
5
6
7
8
9
10
11
12
13const routes = [
// 场景:/search/screens -> /search?q=screens(把路径参数转为查询参数)
{
path: '/search/:searchText', // 带路径参数的路由
redirect: (to) => {
// to.params 包含路径参数,to.query 包含查询参数
console.log(to.params.searchText); // 比如访问 /search/screens,这里输出 screens
// 返回重定向的路径对象(也可以返回字符串,如 `/search?q=${to.params.searchText}`)
return { path: '/search', query: { q: to.params.searchText } };
}
},
{ path: '/search', component: () => import('../views/Search.vue') } // 搜索页面
]访问
/search/手机时,会自动跳转到/search?q=手机,地址栏显示后者的 URL。
虽然还没讲到,但是注意
重定向的原路由(如 /home)上的导航守卫(如
beforeEnter)不会被触发,因为用户访问原路由时会直接跳转到目标路由,只有目标路由的守卫会生效。
1 | // 错误示例:/home 的 beforeEnter 不会执行 |
重定向的路由记录不需要配置
component,因为它从来不会被直接访问,也就没有组件需要渲染。唯一例外:如果路由有
children(嵌套路由)和 redirect,则必须配置
component(作为嵌套视图的容器)。
1 | // 嵌套路由+重定向:必须有 component |
相对重定向
可以基于当前路由的路径,通过函数返回相对路径实现相对重定向,适用于需要修改路径部分内容的场景。
1 | const routes = [ |
别名
URL 的 “别名昵称”
别名是指为一个路由路径设置一个或多个替代路径,当用户访问别名路径时,URL 地址栏不会发生变化,但 Vue Router 会将其视为访问原路径。简单来说,别名是 “用户访问 A,系统可以通过别名认为用户访问的是 B,但地址栏还是 A”。
比如将 / 别名为 /home:
- 用户访问
/home,地址栏仍然显示/home,但系统会渲染/对应的组件。 - 这和重定向的核心区别:重定向会改 URL,别名不会改 URL。
单个别名就是直接为路由配置 alias
属性,指定单个别名路径。
1 | const routes = [ |
访问 /hooome 时,地址栏显示
/hooome,但页面渲染 Home 组件(和访问 /
的效果完全一样)
还可以通过数组为路由设置多个别名,适用于需要多个路径映射到同一个组件的场景。
1 | const routes = [ |
注意:
- 别名以
/开头为绝对别名(基于根路径); - 不以
/开头为相对别名(基于父路由的路径)。
如果路由路径包含参数(如
:id),别名中也需要包含对应的参数,确保参数能正常传递。
1 | const routes = [ |
使用别名时,同一个组件可能对应多个 URL(如 / 和
/home),这会导致搜索引擎认为是重复内容,影响 SEO。
但是可以为页面设置规范链接(canonical link),告诉搜索引擎哪个 URL 是官方的主路径。
在 Vue 组件中可以通过 meta 标签设置:
1 | <!-- Home.vue --> |
将 props 传递给路由组件
我们知道,在 Vue Router 中,路由组件默认可以通过
$route(选项式 API)或 useRoute()(组件式
API)获取路由参数(如 params、query)
1 | <template> |
但是,想没想过,该组件只能在 /user/:id
这个路由下使用,无法在其他地方复用(比如直接在页面中写
<User id="123" />)
组件内部直接依赖路由对象,违反了 组件与路由解耦 的设计原则。
使用 Vue Router 提供的 props
配置,将路由参数作为组件的 props
传入,让组件从路由的固定配置中解放
Vue Router
提供了布尔模式、对象模式、函数模式、命名视图模式(以及额外的
RouterView 插槽传参方式)
布尔模式
传递 route.params 到组件 props
当 props: true 时,路由的 params
参数会被自动映射为组件的 props,适用于
动态路径参数(/user/:id)的场景
首先,定义路由组件,声明对应的 props
1 | <!-- src/views/User.vue --> |
在路由配置中开启 props: true
1 | // src/router/index.js |
当访问 /user/123 时,路由的
route.params.id = '123' 会被自动传入 User 组件的
id prop。
但是布尔模式只处理
route.params,不会处理
route.query(比如 /user?id=123 中的
query.id 不会被传递)。如果路由没有动态参数(如
/user),props: true 不会传递任何参数(因为
params 为空)。
对象模式
当 props
是一个静态对象时,该对象会被原样作为 props
传入组件,适用于传递静态数据(不依赖路由参数的固定值)的场景。
定义路由组件,声明静态 props
1 | <!-- src/views/Promotion.vue --> |
在路由配置中设置 props 为静态对象
1 | // src/router/index.js |
访问 /promotion/from-newsletter 时,组件会收到
{ newsletterPopup: false, activityName: '新年促销' } 的
props。
静态对象中的值不会随路由变化而变化,适合传递固定配置。
但是很少有人用这个,因为无法获取路由的
params、query 等动态数据
如果同时需要静态数据和动态路由数据,不要用对象模式,改用函数模式。
函数模式
props 是一个函数,接收路由对象
route 作为参数,返回一个 props
对象。这是最灵活的模式
例如,我们假如需要将 query 参数映射为 props
比如访问 /search?q=vue,需要把
route.query.q 传递给 SearchUser 组件的 query
prop。
1 | <!-- src/views/SearchUser.vue --> |
1 | // src/router/index.js |
- 访问
/search?q=vue时,组件收到query: 'vue'; - 访问
/search时,组件收到query: ''(默认值)
还可以处理更复杂的参数逻辑,比如根据路由的 params.type
动态设置 props:
1 | props: (route) => { |
注意,props 函数应只依赖 route
参数,不要依赖外部状态,因为 props
函数只在路由变化时执行,如果依赖外部状态,外部状态变化时
props 不会更新。
而且函数必须返回一个纯对象,不能返回响应式对象(如
reactive 或 ref 包装的对象),因为 Vue Router
会自动处理响应式。
命名视图的 props 配置
为多个视图分别设置 props
当路由使用命名视图时,需要为每个命名视图单独配置
props,格式为 { 视图名: 布尔/对象/函数 }。
定义多个命名视图组件
1 | <!-- src/views/UserMain.vue --> |
1 | <!-- App.vue --> |
路由配置(命名视图 + 分别设置 props)
1 | const routes = [ |
通过
<RouterView> 插槽传递 props
除了路由配置中的 props 选项,还可以通过
<RouterView> 的作用域插槽直接传递
props 给路由组件
这种方式适用于需要给所有路由组件传递公共 props 的场景,官方不推荐滥用。
1 | <!-- App.vue --> |
解读一下官方的警告
- 所有组件都会接收 props:这种方式会让所有路由组件都收到传递的 props,即使组件不需要这些 props,这会导致组件的 props 声明冗余,违反 “按需接收” 的原则。
- 优先级问题:如果路由配置中的
props和插槽传递的 props 有重名,插槽传递的 props 会覆盖路由配置的 props(因为组件的 props 是浅合并的)。 - 适用场景:仅在需要传递全局公共数据(如用户信息、全局配置)时使用,优先使用路由配置的
props方式处理路由相关的参数。
匹配当前路由的链接
在 Vue 项目中,我们通常用 <RouterLink>
组件创建导航链接,用于替代原生 <a> 标签
假如一个很常见的情况,就是需要对当前正在访问的路由对应的链接进行视觉区分
Vue Router 为 <RouterLink>
提供了自动添加激活 CSS
类的功能,无需手动判断路由状态,这就是路由链接激活状态的核心作用。
两个激活类:router-link-active
vs router-link-exact-active
<RouterLink> 会为匹配当前路由的链接添加两个 CSS
类,分别对应普通匹配和精确匹配,这是理解激活状态的关键。
我们先通过一个实际的路由例子,理解普通匹配和精确匹配的区别:
1 | // 路由配置 |
对应的导航链接:
1 | <!-- 导航链接 --> |
当当前路径是 /user/erina/role/admin 时:
- 普通匹配:
<RouterLink to="/user/erina">和<RouterLink to="/user/erina/role/admin">都会被判定为匹配,都会添加router-link-active类。 - 精确匹配:只有
<RouterLink to="/user/erina/role/admin">会被判定为精确匹配,会添加router-link-exact-active类;<RouterLink to="/user/erina">不会添加这个类。
简单来说:
router-link-active:包含性匹配,当前路径是该链接路径的子路径(或相等)时触发,会包含祖先路由的链接。router-link-exact-active:完全性匹配,当前路径与该链接路径完全一致时才触发,不包含祖先路由的链接。
RouterLink 匹配当前路由的核心规则
Vue Router 判断一个 <RouterLink>
是否匹配当前路由,遵循以下严格的规则,这些规则决定了激活类是否会被添加
匹配的核心依据是:路由记录 + params 参数
一个链接被判定为匹配当前路由,必须满足两个核心条件:
- 路由记录匹配:链接的
to路径解析后,对应到同一个路由配置记录(即routes数组中的某一项)。 - params 参数匹配:链接的
params(如/user/:username中的username)与当前路由的params完全相同。
注意:
query参数(如/user/erina?tab=profile中的tab)不会影响匹配结果,即使query不同,只要路由记录和params匹配,链接仍会被判定为匹配。hash部分(如/user/erina#top中的#top)也不会影响匹配结果。
嵌套路由中,祖先路由的链接会被匹配
在嵌套路由场景下,指向祖先路由的链接,只要 params
匹配,就会被判定为匹配当前路由(这也是 router-link-active
会作用于祖先链接的原因)。
比如上面的例子中,/user/erina 是
/user/erina/role/admin
的祖先路由,所以前者的链接会被匹配。
别名(alias)会被视为匹配
如果路由配置了别名(如
path: '/', alias: '/home'),那么指向别名的链接(<RouterLink to="/home">)和指向原路径的链接(<RouterLink to="/">),都会被判定为匹配当前路由(只要解析到同一个路由记录)。
重定向(redirect)不会被跟随
如果一个路由配置了重定向(如
path: '/home', redirect: '/'),那么指向 /home
的链接,在判断匹配时不会跟随重定向到
/。也就是说,当当前路径是 /
时,<RouterLink to="/home"> 不会被判定为匹配。
规则 5:路径不需要完全一致(但 params 必须一致)
只要路由记录和 params
匹配,即使路径的表现形式不同(比如通过别名、相对路径),也会被判定为匹配。
自定义激活类名
Vue Router 允许你自定义激活类名,替代默认的
router-link-active 和
router-link-exact-active,满足项目的 CSS 命名规范(比如
Tailwind CSS、BEM 命名法)。
通过 <RouterLink> 的 activeClass 和
exactActiveClass
属性,为单个链接设置自定义类名。这样是局部的自定义
1 | <!-- 局部自定义激活类名 --> |
在创建路由实例时,通过 linkActiveClass 和
linkExactActiveClass 选项,全局设置所有
<RouterLink> 的激活类名
1 | // src/router/index.js |




