「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。
概念
单页面应用就是用 Vue.js + Vue Router 创建的,我们只需要将组件 Component 映射到路由 Routes,然后告诉 Vue Router 在哪里渲染它们即可。Vue Router 能够轻松地管理 SPA 项目中组件的切换。
简单来说就是:Hash 地址与组件之间的对应关系。
注,vue-router 目前只有 3.x 与 4.x 的版本,其中:
- vue-router 3.x 只能结合
vue2进行使用。 - vue-router 4.x 只能结合
vue3进行使用。
本文讲解的 Vue Router 基于 3.x 版本。
快速起步
安装
npm install vue-router --save
使用
① router/index.js
import Vue from 'vue'
// 导入路由模块
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
// 使用路由插件
Vue.use(VueRouter)
// 创建路由对象
const router = new VueRouter({
// Hash 路由模式
mode: 'hash',
// (路由-组件)映射表
routes: [
{
name: 'Home',
path: '/home',
component: Home
},
{
name: 'About',
path: '/about',
component: About
}
]
})
// 导出路由实例
export default router
② main.js
import Vue from 'vue'
import App from './App.vue'
// 相当于导入 ./router/index.js 文件
import router from './router'
Vue.config.productionTip = false
new Vue({
// 将路由实例挂载到 Vue 实例上
router,
render: h => h(App)
}).$mount('#app')
③ App.vue
<!-- 路由占位符 -->
<router-view></router-view>
<!-- 路由链接 -->
<router-link to="/home">首页</router-link>|
<router-link to="/about">关于</router-link>
演示效果:
router 构建选项
routes 映射表
interface RouteConfig = {
path: string,
component?: Component,
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名视图组件
redirect?: string | Location | Function,
props?: boolean | Object | Function,
alias?: string | Array<string>,
children?: Array<RouteConfig>, // 嵌套路由
beforeEnter?: (to: Route, from: Route, next: Function) => void,
meta?: any,
// 2.6.0+
caseSensitive?: boolean, // 匹配规则是否大小写敏感?(默认值:false)
pathToRegexpOptions?: Object // 编译正则的选项
}
mode 路由模式
const router = new VueRouter({
// 默认hash模式, 可选: hash、history、abstract
mode: 'hash'
routes: []
})
区别:
base 基路径
应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 "/app/"。默认值为 /。
linkActiveClass 激活类名
全局配置 <router-link> 默认的激活的 class。被激活的路由链接,默认会应用一个叫做 router-link-active 的类名,开发者可以使用此类名选择器,为激活的路由链接设置高亮的样式:
.router-link-active {
color: tomato;
font-weight: bold;
}
🎈自定义路由高亮的 class 类:
在创建路由的实例对象时,开发者可以基于 linkActiveClass 属性,自定义路由链接被激活时所应用的类名:
// 创建路由对象
const router = new VueRouter({
// 自定义对高亮的 class 类, 默认的 router-link-active 会被覆盖掉
linkActiveClass: 'router-active',
routes: []
})
...
这里仅列出常用的选项,更多请查询官网。
命名路由
编程式导航的
$router.push()函数可以根据命名路由的name属性进行路由跳转。
注意:命名路由的 name 值不能重复,必须保证其唯一性!
{
path: '/movie',
// 使用 name 属性为当前的路由规则定义一个"名称"
name: 'movie',
component: Movie
}
路由重定向与别名
可见启动项目后的默认地址为 http://localhost:8080/,页面一片空白,若要页面一开始就呈现 Home Page 页面,则需要使用路由重定向。
👛重定向:
const router = new VueRouter({
routes: [
// 路由重定向: 将 '/' 重定向到 '/home'
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{ path: '/about', component: About }
]
})
👝起别名:
区别于重定向的 URL 变化。
举个例子:/home 的别名是 /main,意味着,当用户访问 /main 时,URL 会保持为 /main,但是路由匹配则为 /home,就像用户访问 /home 一样。
const router = new VueRouter({
routes: [
// 访问 '/' 时 URL 变为 '/home', 并展示对应组件.
{ path: '/', redirect: '/home' },
// 访问 /main 等同于访问 /home, 但 URL 保持 /main 不变!
{ path: '/home', component: Home, alias: '/main' },
{ path: '/about', component: About }
]
})
路由懒加载
路由懒加载的好处:当访问到某个页面才去加载相关资源,提高页面的访问速度。
{
name: 'About',
path: '/about',
// 路由懒加载
component: () => import('@/views/About.vue')
}
路由链接 & 路由占位符
router-view
router-view: 路由占位符,就是我们要跳转的页面/组件会替换这个占位符。
注:<router-view> 也可以配合 <keep-alive> 一起使用!
<router-view></router-view>
router-link
router-link: 路由链接,用于跳转到指定路径,相当于 a 标签。
<!-- to: 跳转路径 -->
<!-- tag: 渲染为指定标签 -->
<!-- replace: 跳转后不会留下历史记录 -->
<router-link to="/about" tag="button" replace>关于</router-link>
嵌套路由 | 子路由
通过路由实现组件间的嵌套展示,叫做嵌套路由。在父路由规则中,通过 children 属性嵌套声明子路由规则。
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'router-active',
routes: [
{ path: '/', redirect: '/home' },
{ name: 'Home', path: '/home', component: Home },
{
name: 'About',
path: '/about',
component: About,
// 声明子路由规则
children: [
{
name: 'VueJs',
// 切勿写成 '/vuejs'(绝对路径), 'vuejs' 表示相对父路由的路径: /about/vuejs
path: 'vuejs',
component: VueJs
},
{
name: 'vue-cli',
path: 'vue-cli',
component: VueCli
},
{
name: 'VueRouter',
path: 'vue-router',
component: Router
},
{
name: 'Vuex',
path: 'vuex',
component: Vuex
},
]
}
]
})
声明子路由规则后,还需要在父组件中添加子路由占位符 <router-view> 才能使得路由跳转 。
<h2>This is About Page</h2>
<router-link to="/about/vuejs">关于 vue.js</router-link>|
<router-link to="/about/vue-cli">关于 vue-cli</router-link>|
<router-link to="/about/vue-router">关于 vue-router</router-link>|
<router-link to="/about/vuex">关于 vuex</router-link>
<!-- 子路由占位符 -->
<router-view></router-view>
感受下嵌套跳转:
路由参数
路由有 2 种传参方式,分别是
- params 传参:形如
juejin.cn/post/5945 - query 传参:形如
juejin.cn/post?id=5945
params 方式
想要获取 RESTful 风格的路由参数,得把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用 : 定义路由的参数项。比如:
<router-link to="/movie/1">浩克</router-link> |
<router-link to="/movie/2">钢铁侠</router-link> |
<router-link to="/movie/3">蜘蛛侠</router-link>
🚀这类路由称为动态路由。
var router = new VueRouter({
mode: 'hash',
routes: [
{
name: 'Movie',
// 动态路由匹配: 只匹配形如 "/movie/1" 路径、无法匹配 "/movie/1/"、"/movie/" 这类路径
path: '/movie/:mid',
// 指向同一个组件
component: Movie,
}
]
})
通过动态路由匹配的方式渲染出的组件,可以使用 $route.params 对象访问动态匹配的参数值。
this.$route.params.mid
效果:
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 接收动态路由参数。
// => router/index.js
var router = new VueRouter({
mode: 'hash',
routes: [
{
path: '/movie/:mid',
component: Movie,
// 开启props传参: 这个'/:id'中的 id 会被映射到 Movie 组件中 props 的 id 参数
props: true
}
]
)}
在 Movie.vue 组件中接收参数
export default {
// index.js 路由文件的 Movie 组件中 props: true
props: ['mid'],
mounted() {
console.log('mid: ' + this.mid)
},
updated() {
console.log('mid: ' + this.mid)
},
}
演示:
query 方式
通过 ? 拼接参数:
<router-link to="/post?id=14025">Passage1</router-link>
获取参数:
created() {
console.log(this.$route.query);
console.log(this.$route.query.id);
},
query 传参:
$router 与 $route
🚀官网传送门:
前者 $router 是路由组件的导航对象,后者 $route 是路由组件的状态对象。
接下来通过一个实例了解下 $route 对象的部分属性:
$router在下文编程式导航中使用并演示。
<!-- App.vue -->
<router-link to="/movie/1?color=red&age=23">浩克</router-link> |
<router-link to="/movie/2?name=iron&age=21&color=red">钢铁侠</router-link> |
<router-link to="/movie/3?age=16">蜘蛛侠</router-link><br><br>
<!-- Movie.vue -->
<h2>电影ID—— {{ this.$route.params.mid }}</h2>
<h3>this.$route: 路由的参数对象</h3>
<h3>this.$router: 路由的导航对象</h3>
<h3>this.$route.path: {{ this.$route.path }}</h3>
<h3>this.$route.fullPath: {{ this.$route.fullPath }}</h3>
<h3>this.$route.params: {{ this.$route.params }}</h3>
<h3>this.$route.query: {{ this.$route.query }}</h3>
演示结果:
编程式导航
为了搞懂什么是编程式导航,先来简单了解下声明式导航,<router-link :to='xxx'> 导航链接就是声明式导航;而所谓编程式导航就是使用 vue-router 提供的相关 API 进行页面跳转。
| 声明式 | 编程式 |
|---|---|
<router-link :to="{ name: 'movie', params: { mid: 5 } }"> | $router.push({ name: 'movie', params: { mid: 5 } }) |
⭐编程式导航相关 API 如下:
this.$router.push(path)this.$router.replace(path)this.$router.back()this.$router.forward()this.$router.go(n)
$router.push(path)
在 Vue 实例内部,可以通过 $router 访问路由实例。因此你可以调用 this.$router.push(),其他 API 同理。
$router.push() 的参数可以是一个字符串路径,或者一个描述地址的对象。例如:
注:通过
name属性跳转只能是命名路由。
// 字符串 ==> /movie/145?key=value
this.$router.push('/movie/145?key=value')
// 对象 ==> ./movie(相对路径)
this.$router.push({ path: 'movie' })
// 对象 ==> /movie
this.$router.push({ path: '/movie' })
// 命名路由(name属性) ==> /movie/156
this.$router.push({ name: 'movie', params: { mid: '156' }})
// 带查询参数 ==> /movie?plan=private
this.$router.push({ path: '/movie', query: { plan: 'private' }})
// path与params不能同时使用 ==> /movie
this.$router.push({ path: '/movie', params: { mid: '123' }})
💝注意:path 与 params 不能同时使用,否则 params 会被忽略!
$router.replace(path)
用指定路由替换当前路由(用法同 $router.push()),但不会记载到历史记录中。
$router.back()
跳转到历史记录的上一个页面;等价于 $router.go(-1).
$router.forward()
跳转到历史记录的下一个页面;等价于 $router.go(1).
$router.go(n)
可以在浏览历史中前进和后退,n 小于 0 后退,n 大于 0 前进,n 等于 0 刷新当前页面。
导航守卫
全局导航守卫
Vue Router 有 3 个全局守卫:
router.beforeEach:全局前置守卫,进入路由之前router.beforeResolve:全局解析守卫,在beforeRouteEnter调用之后调用router.afterEach:全局后置钩子,进入路由之后
全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
🚀每个守卫方法接收三个参数:
to: 即将要进入的目标 路由对象。from: 当前导航正要离开的路由。next: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next方法的调用参数。next(): 进行管道中的下一个钩子。next(false): 中断当前的导航。next('/')或者next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next传递任意位置对象,且允许设置诸如replace: true、name: 'home'之类的选项以及任何用在router-link的to属性 或router.push中的选项。next(error): 如果传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
🌈确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。举个栗子:
var router = new VueRouter({
routes: []
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
// to: 即将要访问的 $route 路由信息对象
// from: 表示将要离开的 $route 路由信息对象
// next: 1.next(false) 2.next() 3.next('/login')
if (to.path != '/login') {
var token = localStorage.getItem('token')
if (token) {
next()
} else {
next('/login')
}
} else {
next()
}
})
全局解析守卫
你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
// 全局解析守卫
router.beforeResolve((to, from, next) => {
next()
})
全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
// 全局后置钩子
router.afterEach((to, from) => {
// ...
})
路由独享守卫
如果你不想全局配置守卫的话,你可以为某些路由单独配置守卫(路由独享守卫):
路由独享守卫仅有一种:
router.beforeEnter
var router = new VueRouter({
routes: [
{
name: 'movie',
path: '/movie/:mid',
component: Movie,
// 路由独享守卫
beforeEnter: (to, from, next) => {
console.log('hello, movie!');
next()
},
}
]
})
组件内的守卫
可以在路由组件内直接定义以下 3 种路由导航守卫:
beforeRouteEnter: 不能获取组件实例this,因为当守卫执行前,组件实例还没被创建。beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用(即:在动态路由间跳转时)的情况下执行,可以访问组件实例this。beforeRouteLeave: 导航离开该组件的对应路由时调用,可以访问组件实例this。
export default {
data() {
return {}
},
beforeRouteEnter(to, from, next) {
// 进入该路由时执行, 可以通过回调访问this组件
console.log('enter')
next()
},
// 动态路由切换时执行(/movie/:mid)
beforeRouteUpdate(to, from, next) {
// 该路由参数更新时执行:例如从 /movie/1 跳转到 /movie/2 时会执行
console.log('update')
next()
},
// 这个离开守卫通常用来禁止用户在还未保存修改前突然离开, 该导航可以通过 next(false) 来取消
beforeRouteLeave(to, from, next) {
// 离开该路由时执行
console.log('leave')
next()
},
}
❤️/ END / 如果本文对你有帮助,点个「赞」支持下吧。