Vue2教程详解八(路由vue-router3)
目录简介安装与基本使用路由器的工作模式嵌套路由params传参和query传参query传参params传参命名路由路由组件传参(props配置)对象模式布尔模式函数模式router-link的replace属性编程式路由/导航缓存路由组件路由守卫/导航守卫全局守卫独享守卫组件内守卫路由元信息完整的导航解析流程简介Vue-router是vue的一个插件库,专门用来实现SPA(single page
目录
简介
Vue-router是vue的一个插件库,专门用来实现SPA(single page web application,单页web应用)应用;
路由其实就是一组映射关系(key-value),key为路径,value可能是function或者component
安装与基本使用
2022年2月7日以后,vue-router的默认安装版本为版本4,即vue-router4只能用在vue3中,vue-router3才能用在vue2中;
npm install vue-router // 默认安装的是版本4
npm install vue-router@3 // 安装的是版本3
如果要在一个模块化工程中使用它,必须要通过Vue.use()明确的安装路由功能(Vue-router是个插件):
// router/index.js文件
import Vue from 'vue'
import Router from 'vue-router'
// 导入组件
import Login from '../view/gsys/Login'
// 应用路由插件
Vue.use(Router)
// 创建一个路由器
export default new Router({
routes: [{
path: '/login',
component: Login
}
]
})
// main.js文件
import Vue from 'vue'
import App from './App'
import router from './router'
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
// app.vue文件
<div id="app">
<p>
//通过标签router-link组件来导航,to表示导航到的路径,active-class属性实现高亮样式,该标签默认会被渲染成一个<a>标签
<router-link to="/home" active-class="active">首页</router-link>
</p>
// 路由出口----路由匹配到的组件将会被渲染在此处
<router-view></router-view>
注意:
- 路由组件通常存放于pages文件夹,一般组件一般存放于components文件夹;
- 通过切换组件,“隐藏”了的路由组件,默认是被销毁的,所以组件的生命周期函数皆不起作用了,需要的时候再去挂载;
- 每个组件都有自己的$route属性,里面存储的是自己的路由信息;
- 整个应用只有一个router,可通过组件的$router属性获取到;
- 常规参数只会匹配被/分割的URL片段中的字符。若想匹配任意路径,可以使用通配符(*)
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
注意使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放到最后。路由{path: '*'}通常用于客户端404错误页;
当使用一个通配符时,$route.params内会自动添加一个名为pathMatch参数,它包含了URL通过通配符被匹配到的部分:
// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
路由器的工作模式
vue-router默认hash模式(url中#及其后面的内容就是hash值)----使用url的hash来模拟一个完整的url,于是当url改变时,页面不会被重新加载;且兼容性较好,但缺点是不美观~
如果不想要很丑的hash,可使用history模式,这种美观,但兼容性较差,更要命的是若后台未配置,则直接访问如http://oursite.com/user/id会返回404;配置history模式代码如下:
const router = new VueRouter({
mode: 'history',
routes: [...]
})
嵌套路由
配置嵌套路由,使用children配置项:
// 配置路由规则
routes: [{
path: '/login',
component: Login
children: [
{
path: 'user',
component: User
}
]
}]
// 跳转
<router-link to="/home/user">用户</router-link>
注意:
配置子路径时,path中不需要加/(因为以/开头的嵌套路径会被当作根路径,可让我们充分的使用嵌套组件而无需设置嵌套的路径),跳转中to的路径要加上!
params传参和query传参
query传参
query传参不会破坏路由规则,只需将参数配置在跳转标签中即可;
// 跳转
// to的字符串写法
<router-link :to="`/home/user?id=${id}&name=${name}`">用户</router-link>
// to的对象写法
<router-link :to="{
path: '/home/user',
query: {
id: id,
name:name
}">
用户
</router-link>
// 接收参数
$route.query.id
$route.query.name
params传参
params传参要破坏路由配置规则,在path中加入:要传入的参数;
// 配置路由规则
routes: [{
path: '/login',
component: Login
children: [
{
path: 'user/:id/:name',
component: User,
name: 'user' // 命名路由
}
]
}]
// 跳转
// to的字符串写法
<router-link :to="`/home/user/${id}/${name}`">用户</router-link>
// to的对象写法
<router-link :to="
{
name: 'user', // 命名路由
query: {
id: id,
name: name
}
}">用户
</router-link>
并且使用params传参的对象形式时,子页面的router-link不允许使用path了,必须要使用命名路由name。
// 接收参数
$route.params.id
$route.params.name
可看另一篇文章总结params传参与query传参总结
命名路由
设置命名路由后,就可在设置跳转路由地址的地方使用,将长长的path路径替代为name属性:
const router = new VueRouter({
routes: [
{
path: '/user/address',
name: 'address',
component: Address
}
]
})
要链接到一个命名路由,可以改router-link的to属性传一个对象;
// 简化前
<router-link :to="{
path:'`/user/address?userId=${userId}`',
query: {userId: 12}
}">
地址
</router-link>
// 简化后
<router-link :to="{
name:'address',
query: {userId: 12}
}">
地址
</router-link>
路由组件传参(props配置)
在组件中使用$route会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的url上使用,限制了其灵活性,可使用props将组件和路由解耦,有三种方式:
对象模式
若props值是对象,则该对象中所有的key-value的组合最终都会通过props传给组件,这种方式一般用来传递静态参数;
const router = new VueRouter({
routes: [
{
path: '/promotion/from-newsletter',
component: Promotion,
props: { id: 12 }
}
]
})
布尔模式
若props值是布尔值,若布尔值为true,则就会将路由收到的所有params参数通过props传给组件(适用于params传参);
函数模式
若props值为函数,则该函数返回的对象中每一组key-value值都会通过props传给组件;
const router = new VueRouter({
routes: [
{
path: '/search',
component: SearchUser,
props(route) {
return {
id: route.query.id
}
}
]
})
三种方式对应组件使用:
<template>
<div>{{groupId}}</div>
</template>
<script>
export default {
props: ["id"]
}
</script>
router-link的replace属性
该属性是用来控制路由跳转时操作浏览器历史记录的模式;
浏览器的历史记录有两种写入方式:分别为push和replace,其中push是追加历史记录,replace是替换当前记录,路由跳转时默认为push;
使用如下代码即可开启replace模式,开启了replace模式后,则该页面在浏览器前进后退中则不会出现,即点击浏览器后退按钮后,不会再返回之前的设置了replace属性的页面;
<router-link replace>首页<router-link/>
编程式路由/导航
除了 使用router-link标签创建a标签来定义导航链接,还可借助router的实例方法,通过编写代码来实现。使用$router.push或$router.replace实现路由跳转;
该对象的参数可以是一个字符串路径,或者是一个描述地址的对象;
// 字符串
this.$router.push('home')
// 对象
this.$router.push({ path: 'home' })
// 命名的路由
this.$router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
this.$router.push({ path: 'register', query: { plan: 'private' }})
router.replace同理;
this.$router.forward() // 前进一步
this.$router.back() // 后退
this.$router.go(n) // 可前进(正数),可后退(负数)
缓存路由组件
前面说过,在切换路由组件时,默认会销毁前一个组件,但是有的时候,如果想保留前一页面中的内容,则不希望在前进页面的时候,将该页提交的内容全部删除掉,这种场景下,可通过缓存路由keep-alive来实现;
在路由出口处设置即可:
<keep-alive>
</router-view>
</keep-alive>
若想仅对某些页面缓存,则可设置include属性,参数值是设置缓存的页面/组件名;
<keep-alive include="home">
</router-view>
</keep-alive>
缓存的页面多的话,要设置成数组形式:
<keep-alive include="['home','user']">
</router-view>
</keep-alive>
路由守卫/导航守卫
路由守卫用于对路由进行权限控制,主要分为全局守卫/独享守卫与组件内守卫;
全局守卫
全局守卫分为全局前置守卫/全局解析守卫/全局后置守卫;
- 全局前置守卫
全局前置守卫router.beforeEach在路由初始化时执行,每次路由切换前执行。接收三个参数:
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的to prop或router.push中的选项。
next(error)
: (2.4.0+) 如果传入 next
的参数是一个 Error
实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
确保next函数在任何给定的路由守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错;
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
-
全局解析守卫
在版本2.5.0+可以使用router.beforeResolve注册一个全局守卫。这和router.beforeEach类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由被解析之后,解析守卫就被调用;
- 全局后置守卫
全局后置守卫router.aftereach用于初始化的时候被调用,每次路由切换之后被调用,与前两个守卫不同的是,它不会接收next函数,并且也不会改变导航本身,因为他已经切换完了;
独享守卫
可在路由配置上直接定义beforeEnter守卫,用于对该路由进行权限控制;参数与全局前置守卫一样;
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件内守卫
组件内守卫是指定义在组件内,在路由跳转至组件内部或离开组件的时候的权限控制;有三种beforeRouterEnter/beforeRouterUpdate(2.2新增)/beforeRouteLeave
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
离开守卫通常用于禁止用户在还未保存修改前突然离开,该导航可以通过next(false)来取消;
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
路由元信息
可在需要权限校验的路由配置项上配置meta元信息,如:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
可通过$route.meta.xxx进行访问,也可以通过$route.matched来检查路由记录中的meta字段(一个路由匹配到的所有路由记录都会暴露成$route对象的$route.matched数组)。
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保一定要调用 next()
}
})
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
更多推荐
所有评论(0)