Vue Router
第一章:初识
一、概述
1. SPA
单页面应用程序(英文名:Single Page Application)简称 SPA。顾名思义,指的是一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能与交互都在这唯一的一个页面内完成。这就需要用到路由。
一个路由(route)就是一个映射关系(key - value),多个路由需要路由器(router)进行管理。
总结:前端路由 key 是路径,value 是组件。
官方文档:Vue Router
2. 快速入门
1)安装 Vue Router
# 安装最新版 vue-router
npm i vue-router
# vue2 项目,要指定版本安装
npm i vue-router@3.5.2说明
2022 / 07 以后,安装 vue-router 时,默认版本改为了 4,而 4 版本只能在 Vue3 中使用。只有 vue-router3 才能在 Vue2 使用。
2)引入与应用插件
@/main.js
// 1. 导入所需模块
import Vue from 'vue'
import VueRouter from 'vue-router'
// 2. 调用 Vue.use() 函数,将 VueRouter 安装为 Vue 的插件
Vue.use(VueRouter)3)编写 router 配置项
新建 @/router/index.js 文件,编写路由配置。
// 引入VueRouter
import VueRouter from 'vue-router'
// 引入Luyou组件
import About from '../components/About'
import Home from '../components/Home'
// 配置路由规则
const routes = [
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
// 创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
mode: 'history', // 或者是 hash
routes,
})
// 暴露router
export default router4)在 main.js 中创建 vm 时传入 router 配置项
......
// 引入router
import router from './router'
......
// 创建vm
new Vue({
el:'#app',
render: h => h(App),
router
})5)实现切换(active-class 可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link>6)指定展示位置
<router-view></router-view>任何子路由都是在其父路由的组件中切换显示,不管是多少层的路由嵌套。因此,在其父路由上需要写上 <router-view /> 承载容器来存放子路由组件。
几个注意点
① 路由组件通常存放在 pages|views 文件夹,一般组件通常存放在 components 文件夹。
② 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
③ 每个组件都有自己的 $route 属性,里面存储着自己的路由信息。
④ 整个应用只有一个 router,可以通过组件的 $router 属性获取到。
二、必知
1. <router-link> 其他属性
1)replace 属性
① 作用:控制路由跳转时操作浏览器历史记录的模式。
② 浏览器的历史记录有两种写入方式:push 和 replace。push 是追加历史记录,replace 是替换当前记录。路由跳转时候默认为 push。
③ 如何开启 replace 模式?
<!-- 完整写法 -->
<router-link :replace="true" .......>News</router-link>
<!-- 简写 -->
<router-link replace .......>News</router-link>2)链接高亮 (排它)
为什么不用 a 标签,而用 router-link 标签?
- 使用 router-link 也会解析为 a 标签。
- 会给当前访问的超链接,自动加两个类名(通过给下述两个类添加样式,实现导航链接的高亮效果)。
- router-link-exact-active --- 精确匹配类名
- router-link-active --- 模糊匹配类名
如何更改默认给我们添加的类名?
const router = new VueRouter({
routes: [...],
linkActiveClass: "类名1",
linkExactActiveClass: "类名2"
})实用技巧
假如在 App.vue 文件中编写了 router-link-active 高亮样式。
.router-link-active {
color: red !important;
}页面有多个歌手列表,每一项路由路径是 /discover/artlist?id=x。当我点击了孙楠时,会给其他歌手添加一个 router-link-active 类,其他歌手也会高亮。

怎么办?在 artlist 组件里可以这样书写。
a {
color: #424242 !important;
}
a.router-link-exact-active {
color: red !important;
}2. 缓存路由组件
1)作用:让不展示的路由组件保持挂载,不被销毁。
2)具体编码
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>include 写的是组件名(组件自身的 name 属性),不写 include 属性,将会缓存所有组件。
3. 路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们可以把不同的代码组件分割成不同的代码块,然后在访问的时候才加载对应的代码块,这样会更加灵活。也可以提高首屏的渲染效率。
实际上还是我们前面讲到的 webpack 的打包知识,而 Vue Router 默认就支持动态加载组件。这是因为 component 可以传入一个组件,也可以接收一个函数,该函数要返回一个 Promise。而 import 函数就是返回一个 Promise。这样就会更加高效。
const routes = [
{ path: '/', redirect: '/home' },
{ path: '/home', component: () => import('./pages/Home.vue') },
{ path: '/about', component: () => import('./pages/About.vue') }
]我们会发现分包是没有一个很明确的名称的,其实 webpack 从 3.x 开始支持对分包进行命名。
component: () => import(/* webpackChunkName: "home-chunk" */ '../pages/Home.vue')4. 两个新的生命周期钩子
1)作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2)具体名字:
① activated 路由组件被激活时触发。
② deactivated 路由组件失活时触发。
第二章:路由规则
一、多级路由 (嵌套路由)
1. 常见用法
1)配置路由规则,使用 children 配置项
注意:看是几级路由不能看路径,要看在路由配置里是否在 children 里。
routes:[
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[ // 通过children配置子级路由
{
path:'news', // 此处一定不要写: /news
component:News
},
{
path:'message', // 此处一定不要写: /message
component:Message
}
]
}
]2)跳转(要写完整路径)
<router-link to="/home/news">News</router-link>2. 细节
上面例子中,路由为 /home 时,Home 组件中的 router-view 里面什么都不会呈现,因为没有匹配到嵌套路由。想让它呈现,怎么办?
routes: [
// ...
{
path: '/home',
component: Home,
children: [
{
path: '',
component: HomeHome
},
{
path: 'news',
component: News
}
// ...其他子路由
]
}
]二、命名路由
1. 常见用法
1)作用:可以简化路由的跳转。
2)如何使用
① 给路由命名
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' // 给路由命名
path:'welcome',
component:Hello,
}
]
}
]
}② 简化跳转
<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>2. 细节
在一些场景中,可能希望导航到命名路由而不导航到嵌套路由。例如,想导航 /user/:id 而不显示嵌套路由。那样的话,可以命名父路由,但请注意重新加载页面将始终显示嵌套的子路由,因为它被视为指向路径/users/:id 的导航,而不是命名路由。
const routes = [
{
path: '/user/:id',
name: 'user-parent',
component: User,
children: [{ path: '', name: 'user', component: UserHome }],
},
]现象:
// 通过名称导航
router.push({ name: 'user-parent', params: { id: 123 } })
// Vue Router 思考: "用户明确指定了 name: 'user-parent'"
// → 只导航到父路由, 不管子路由
// 通过路径访问 /user/123
// Vue Router 思考: "根据路径匹配规则"
// → /user/123 匹配父路由的 path: '/user/:id'
// → 发现有 path: '' 的子路由
// → 自动匹配并显示子路由三、其他
1. 路由重定向
{
path: '/',
redirect: '/discover/tuijian' // 重定向路由为路由 path
}
{
path: '/',
redirect: { name: '/discover/tuijian' } // 重定向路由为命名路由
}在写 redirect 的时候,可以省略 component 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 children 和 redirect 属性,它也应该有 component 属性。
2. 404 处理
如果想匹配任意路径,可以使用自定义的 路径参数 正则表达式,在 路径参数 后面的括号中加入正则表达式:
const routes = [
// 将匹配所有内容并将其放在 `route.params.pathMatch` 下
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
// 将匹配以 `/user-` 开头的所有内容,并将其放在 `route.params.afterUser` 下
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]{
path: '*',
component: () => import('@/views/NotFound.vue')
}第三章:路由传参
一、RouterLink 导航
1. query 参数
① 传递参数
<!-- 跳转并携带query参数, to的字符串写法 -->
<router-link to="/home/message/detail?id=666&title=你好">跳转</router-link>
<router-link :to="`/home/message/detail?id=${id}&title=${title}`">跳转</router-link>
<!-- 跳转并携带query参数, to的对象写法 -->
<router-link
:to="{
path: '/home/message/detail',
query: {
id: 666,
title: '你好'
}
}"
>跳转</router-link>② 接收参数
$route.query.id
$route.query.title2. params 参数
① 配置路由,声明接收 params 参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
path: 'message',
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', // 使用占位符声明接收params参数
component:Detail
}
]
}
]
}
// 注: 在配置路由的时候, 在占位的后面加上一个问号, 表示params可以传递或者不传递
// 比如: path: '/search/:keyword?',② 传递参数
<!-- 跳转并携带params参数, to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<router-link :to="`/home/message/detail/${m.id}/${m.title}`"></router-link>
<!-- 跳转并携带params参数, to的对象写法 -->
<router-link
:to="{
name: 'xiangqing',
params: {
id: 666,
title: '你好'
}
}"
>跳转</router-link>特别注意:路由携带 params 参数时,若使用 to 的对象写法,则不能使用 path 配置项,必须使用 name 配置!
③ 接收参数
$route.params.id
$route.params.title在 template 中,直接通过 $route.params 获取值。
在 created 中,通过 this.$route.params 获取值。
在 setup 中,需要用 vue-router 库的一个 useRoute hook。该 Hook 会返回一个 Route 对象,对象中保存着当前路由相关的的值。
3. props 配置
作用:让路由组件更方便的收到参数。
{
name: 'xiangqing',
path: 'detail/:id',
component: Detail,
// 第一种写法: props值为对象, 该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props: {a:900}
// 第二种写法: props值为布尔值, 布尔值为true, 则把路由收到的所有params参数通过props传给Detail组件
// props: true
// 第三种写法: props值为函数, 该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route) {
return {
id: route.query.id,
title: route.query.title
}
}
}面试题
路由传递参数(对象写法)path 是否可以结合 params 参数一起使用?
答:路由跳转传参的时候,对象的写法可以是 name、path 形式,但需要注意的是,path 这种写法不能与 params 参数一起使用。
如何指定 params 参数可传可不传?
如果路由要求传递 params 参数,但是你就不传递 params 参数,URL 就会有问题 比如: 配置路由的时候,占位了 (params 参数),但是路由跳转的时候就不传递,路径就会出现问题: http://localhost:8080/#/?k=QWE http://localhost:8080/#/search?k=QWE (正常情况下) 如何指定 params 参数可以传递、或者不传递,在配置路由的时候,在占位的后面加上一个问号params 参数可以传递也可以不传递,但是如果传递的是空串,如何解决?
使用 undefined 解决 params 参数可以传递、不传递(空的字符串)。
javascriptthis.$router.push({ name: 'search', params: { keyword: '' || undefined }, query: { k: this.keyword.toUpperCase() } });路由组件能不能传递 props 数据?
可以总共有三种写法。
javascript// 布尔值写法: params 转 props props: true, // 对象写法: 额外的给路由组件传递一些 props props: { a: 1, b: 2 }, // 函数写法: 可以params参数、query参数,通过props传递给路由组件 props: ($route) => { return { keyword: $route.params.keyword, k: $route.query.k } }
二、编程式路由导航
1)作用:不借助 <router-link> 实现路由跳转,让路由跳转更加灵活。
2)API:在配置路由时,创建了一个 router 对象,接着把它放到了 Vue 的配置对象中。之后 Vue 会自动把它注入到组件实例上,属性名为 $router(这个值是全局唯一的)。
// *************************************************************** 字符串
this.$router.push('路由路径')
this.$router.replace('路由路径')
// *************************************************************** 对象
// ********************************************* push
// 写法一:对象格式,用 path 进行跳转
this.$router.push({
path: '完整路由地址',
query: {
参数: '值',
参数: '值'
}, // query参数会拼接成 /xxxxx?参数=值&参数=值
// params: {
// 参数: '值',
// 参数: '值'
// }, // 有问题,不用。参数不能拼接到url后面
})
// 写法二:对象格式,用 name 进行跳转
this.$router.push({
name: '路由的name值',
query: {
参数: '值',
参数: '值'
}, // /xxxxx?参数=值&参数=值
params: {
参数: '值',
参数: '值'
}, // 参数会拼到url后面 /xxxxx/3/绿皮书
})
// 带 hash,结果是 /about#team
this.$router.push({ path: '/about', hash: '#team' })
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
// ********************************************* replace
this.$router.replace({
name:'路由的name值',
params:{
参数: '值',
参数: '值'
}
})
// *************************************************************** 前进后退
this.$router.forward() // 前进
this.$router.back() // 后退
this.$router.go(n) // 可前进也可后退 里面传递的是数字,正数前进几步,负数后退几步| 跳转类型 | query 参数(id=1&name=xx) | params 参数(/movie/1) |
|---|---|---|
| path | √ | × |
| name | √ | √ |
| 完整字符串 | — | — |
第四章:路由守卫
作用:对路由进行权限控制。
分类:全局守卫、独享守卫、组件内守卫
每个守卫方法接收三个参数:
to: Route:即将要进入的目标路由对象。from: Route:当前导航正要离开的路由。next: Function:一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。next():进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。next(false):中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。next('/')或者next({ path: '/' }):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next传递任意位置对象,且允许设置诸如replace: true、name: 'home'之类的选项以及任何用在router-link的toprop 或router.push中的选项。next(error):(2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
在 Vue3.x 中,不在推荐使用 next() 方法了。而是使用返回值:
- false:取消当前导航。
- 不返回或者 undefined:进行默认导航。
- 返回一个路由地址:
- 可以是一个 string 类型的路径。
- 可以是一个对象。对象中包含 path 、query 、params 等信息。
1)全局守卫
@/router/index.js
// 全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to, from, next)=>{
console.log('beforeEach', to, from)
if(to.meta.isAuth){ // 判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'hj44d45fh6*'){ // 权限控制的具体规则
next() // 放行
}else{
alert('暂无权限查看')
next({name:'help'})
}
}else{
next() // 放行
}
})
// 全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to, from)=>{
console.log('afterEach', to, from)
if(to.meta.title){
document.title = to.meta.title // 修改网页的title
}else{
document.title = 'vue_test'
}
})2)独享守卫
只作用于某个特定的路由,这些守卫定义在路由配置对象上。只有前置独享守卫,无后置独享守卫。
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})可以把全局守卫的前置守卫代码放到独享守卫里面,如下:
beforeEnter(to, from, next){
console.log('beforeEnter', to, from)
if(to.meta.isAuth){ // 判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'hj44d45fh6*'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
}3)组件内守卫
只作用于某个特定的组件内部,这些守卫可以在组件内使用。
// 进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
// 离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}6. 路由器的两种工作模式
1)对于一个 url 来说,什么是 hash 值?url 路径上的 # 及其后面的内容就是 hash 值。
2)hash 值不会包含在 HTTP 请求中,即 hash 值不会带给服务器。
3)hash 模式:
① 地址中永远带着 # 号,不美观。
② 若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法。
③ 兼容性较好。
4)history 模式:
① 地址干净,美观 。
② 兼容性和 hash 模式相比略差。
③ 应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。
注意:前端人员如果使用 Node.js 部署时,可以使用 connect-history-api-fallback 包来解决问题。
第五章:进阶
一、滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置。vue-router 可以自定义路由切换时页面如何滚动。
当创建一个 Router 实例时,可以提供一个 scrollBehavior 方法:
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})该函数可以返回一个 ScrollToOptions 位置对象:
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 始终滚动到顶部
return { top: 0 }
},
})5)router 其他配置项
{
path: '/about',
name: 'about-router',
component: () => import('../pages/About.vue'),
meta: {
name: 'why',
age: 18
}
}在组件中访问 meta 属性
<template>
<div>
<h1>{{ name }}</h1>
<p>{{ age }}</p>
</div>
</template>
<script>
export default {
computed: {
name() {
return this.$route.meta.name;
},
age() {
return this.$route.meta.age;
}
}
};
</script>