两种路由模式
- hash模式
{ history: createWebHashHistory() }
- history模式
{ history: createWebHistory() }
内置组件
router-link
: 切换到指定的组件。
下面介绍一下他的常用属性
to
属性:是一个字符串,或者是一个对象
<!-- 字符串 --> <router-link to="/home">Home</router-link> <!-- 渲染结果 --> <a href="/home">Home</a> <!-- 使用 v-bind 的 JS 表达式 --> <router-link :to="'/home'">Home</router-link> <!-- 同上 --> <router-link :to="{ path: '/home' }">Home</router-link> <!-- 命名的路由 --> <router-link :to="{ name: 'user', params: { userId: '123' }}">User</router-link> <!-- 带查询参数,下面的结果为 `/register?plan=private` --> <router-link :to="{ path: '/register', query: { plan: 'private' }}"> Register </router-link>
replace
属性:设置 replace 属性的话,当点击时,会调用 router.replace(),而不是 router.push()。
active-class
属性:设置激活a元素后应用的class,默认是router-link-active。如果父路径相同就会添加给a元素这个class。
exact-active-class
属性:链接精准激活时,应用于渲染的<a>
的 class,默认是router-link-exact-active。只有父子路径都相同时,才会添加这个class。
custom
属性: 如果设置为ture, 他表示将当前router-link
包裹的元素外的a标签去除。在使用v-slot
创建自定义 RouterLink 时很有用。我们可以自定义跳转逻辑。
v-slot
指令: 接收作用域插槽传递的props。用于自定义router-link。 在vue-router3.x的时候,router-link有一个tag属性,可以决定router-link到底渲染成什么元素。但是在vue-router4.x开始,该属性被移除了。而给我们提供了更加具有灵活性的v-slot的方式来定制渲染的内容。
v-slot
如何使用呢?
- 首先,在
router-link
上设置custom
属性,表示我们整个元素要自定义。如果不写,那么自定义的内容会被包裹在一个 a 元素中。
- 其次,我们使用v-slot来作用域插槽来获取内部传给我们的值:
- href:解析后的 URL。
- route:解析后的规范化的route对象。
- navigate:触发导航的函数。相当于
router-link
中的to
属性做的事情。跳转页面到指定路径。
- isActive:是否匹配的状态。相当于
router-link
中的active-class
属性,如果匹配到了自动添加router-link-active
class属性。我们可以通过isActive的值来自定义active-class值。
- isExactActive:是否是精准匹配的状态。相当于
router-link
中的exact-active-class
属性,如果匹配到了自动添加router-link-exact-active
class属性。我们可以通过isExactActive的值来自定义exact-active-class值。
<router-link to="/home" v-slot="props" custom > <button @click="props.navigate">{{props.href}} 跳转到home</button> <button @click="props.navigate">跳转到home, 可以将props.navigate添加到多个元素上</button> <span :class="{'active': props.isActive}">{{props.isActive}}</span> <span :class="{'active': props.isActive}">{{props.isExactActive}}</span> <!-- <p>{{props.route}}</p> --> </router-link>
router-view
: 切换路径,他就自动匹配到对应的组件。
name
: 当我们需要多个视图,而不是嵌套展示。我们就可以通过name属性来匹配到route中提供的components
组件。
v-slot
指令: 接收作用域插槽传递的props。可以用于 和 组件来包裹你的路由组件。
其中提供两个props:
- Component:要渲染的组件。即当前路由匹配到的组件。
<router-view v-slot="{ Component, route }"> <transition> <keep-alive> <component :is="Component" /> </keep-alive> </transition> </router-view>
- route:解析出的标准化路由对象。即当前的路由对象。
路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。也可以提高首屏的渲染效率。
其实路由懒加载就是给component属性传入一个工厂函数,并且返回一个promise对象。
component: () => import("../pages/home.vue")
动态路由
记得当时学习vue2的时候,了解到动态路由一头雾水。其实我们可以把它理解为为url传递参数,并在页面中获取到该参数,做一些事情。
很多时候我们需要将给定匹配模式的路由映射到同一个组件:例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但是用户的ID是不同的。在Vue Router中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数。
那么在User中如何获取到对应的值呢?
- 在template中,直接通过 $route.params获取值。
- 在created中,通过 this.$route.params获取值。
- 在setup中,我们要使用 vue-router库给我们提供的一个hook
useRoute
。该Hook会返回一个Route对象,对象中保存着当前路由相关的值。
我们也可以在同一个路由中设置有多个 路径参数,它们会映射到 $route.params
上的相应字段。$route.params
上保存的就是动态参数对象。
错误页面处理
当我们访问没有路由映射的页面时,我们需要给用户一些提示。
对于哪些没有匹配到的路由,我们通常会匹配到固定的某个页面。比如NotFound的错误页面中,这个时候我们可编写一个根路径下的动态路由用于匹配所有的页面。我们可以通过 $route.params.pathMatch
获取到传入的参数。
// 这种匹配是将错误路径以"/"作为分隔符,分割错误参数,并将其传入到patchMath数组中。 { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound } // 这种匹配是将错误路径当成整体传入到pathMatch属性中 { path: '/:pathMatch(.*)', name: 'NotFound', component: NotFound }
嵌套路由
有时候,一个页面也可能出现多个组件间相互切换,我们就可以定义子路由来匹配到这个组件。注意:我们也需要在对应的位置设置router-view
来渲染子路由匹配到的组件。
如何来定义子路由呢? 我们只需要在路由映射对象中定义一个children属性然后来定义子路由的映射即可。但是有一些注意事项:
path
属性不需要加/
, 如果加上了/
,它将映射的是一级路由。
- 重定向传入的路由是完整的
href
。
{ path: "/home", name: "home", component: () => import(/* webpackChunkName: "home-chunk" */"../pages/Home.vue"), children: [ { path: "", redirect: "/home/message" }, { path: "message", component: () => import("../pages/HomeMessage.vue") }, { path: "goods", component: () => import("../pages/HomeGoods.vue") } ] }
路由跳转时参数传递
- 通过params, 即上面讲到的动态路由。
- 通过
$route.params
获取
- 通过query, 当我们跳转路由时,不管是通过
router-link
还是通过$router.push()
等API, 我们都可以传入一个query对象,并且在query对象中设置传递的参数。
- 通过
$route.query
获取
- 通过meta, 在我们定义路由映射表的时候,我们可以在meta字段中定义一些数据。
- 通过
$route.meta
获取
- 通过props,在我们定义路由映射表时,我们可以传递一些props给组件。这个就可以作为组件的props。
const routes = [ { path: '/search', component: SearchUser, // 这里可以返回一个函数,动态传参。也可以直接返回一个props props: route => ({ query: route.query.q }) } ]
编程式导航
我们知道,切换路由我们可以通过router-link
组件来完成。但是我们大多数还是通过书写代码来切换路由的。
- 在options API中,我们可以通过vue-router提供的
this.$router
对象调用其API来切换路由。
- 在setup中,我们可以通过vue-router提供的hook
useRouter
来调用其API切换路由。
setup() { const router = useRouter() const routerSkip = () => { router.push('/about') router.replace('/about') } }
切换路由的API
push
: 这个方法会向 history 栈添加一个新的记录,可以点击回退上一个路径。
replace
: 它在导航时不会向 history 添加新记录。取代当前的路径。
go
: 该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步。
back
: 在历史堆栈中后退一步。相当于go(-1)
。
forward
: 在历史堆栈中前进一步。相当于go(1)
。router.push
,router.replace
和router.go
是window.history.pushState
、window.history.replaceState
和window.history.go
的翻版,它们确实模仿了window.history
的 API。
注意:前两个API传入的参数和router-link
中to属性是一样的。当同时传入path
和params
,这会忽略params
动态添加路由
某些情况下我们可能需要动态的来添加路由。比如根据用户不同的权限,注册不同的路由。这个时候我们可以使用一个方法 addRoute
- 添加一级路由。我们只需要向
addRoute
中添加一个参数。如果路由有一个name
字段,并且已经有一个与之名字相同的路由,它会先删除之前的路由。
// 动态添加路由 const categoryRoute = { path: "/category", component: () => import("../pages/Category.vue") } // 添加顶级路由对象 router.addRoute(categoryRoute);
- 添加二级路由。我们需要传入当前路由(即路由的name字段)和添加的子路由。
// 添加二级路由对象 router.addRoute("home", { path: "moment", component: () => import("../pages/HomeMoment.vue") })
动态删除路由
与动态添加路由对应。
删除路由有以下三种方式:
- 方式一:添加一个name相同的路由。
- 方式二:通过removeRoute方法,传入路由的名称。
- 方式三:通过addRoute方法的返回值回调。
当路由被删除时,所有的别名和子路由也会被同时删除
导航守卫
vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。在跳转之前,之后等状态时,做一些逻辑处理。类似于Express, Koa中的中间件。
下面我们来讲一讲全局前置守卫router.beforeEach
。其他的守卫用法一致,只是调用时机不同而已。
它有三个参数:
to
:即将进入的路由Route对象。
from
:即将离开的路由Route对象。
next
(可选):允许路由正常跳转。在vue-router3.x中,他是必传的,但是在4.x官方已不再建议使用。当不满足条件时,我们可以通过返回一个路径字符串或者一个对象来指定跳转。满足条件时,他会自动跳转。
它有返回值:
- false:取消当前导航跳转。
- 不返回或者undefined:进行默认导航跳转。
- 返回一个路由地址。可以是一个string类型的路径,也可以是一个对象,对象中包含path、query、params等信息。 下面这个例子,就是当用户未登录的时候,不管访问任何页面,我们都让其跳转到login页面。
router.beforeEach((to, from) => { if (to.path !== "/login") { const token = window.localStorage.getItem("token"); if (!token) { return "/login" } } })
其他的导航守卫,请访问官网:next.router.vuejs.org/zh/guide/ad…
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。
- 调用全局的
beforeEach
守卫。
- 在重用的组件里调用
beforeRouteUpdate
守卫(2.2+)。
- 在路由配置里调用
beforeEnter
。
- 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。
- 调用全局的
beforeResolve
守卫(2.5+)。
- 导航被确认。
- 调用全局的
afterEach
钩子。
- 触发 DOM 更新。
- 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。