Vue-Router
Vue-router做路径匹配时支持动态片段、全匹配片段以及查询参数(片段指的是URL中的一部分)
对于解析过的路由,这些信息都可以通过路由上下文对象(从现在起,我们会称其为路由对象)访问。
在使用了vue-router的应用中,路由对象会被注入每个组件中,赋值为this.$route,并且当路由切换时,路由对象会被更新
脚手架中添加vue-router
- 在路由定义文件中,通过
Vue.use添加组件选项,并且该方法必须在new Vue()启动应用之前完成
Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router);
export default new Router({
mode: 'history', // 去掉路由中的#号
routes: [
{
path: '/',
name: 'Home',
component: Home
},
]
})
- 在
Vue.new初始化时,将可导出router对象注入到Vue根实例中
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
new Vue({
el: '#app',
router, // 注入router
components: {App},
template: '<App/>'
})
这样就可以了
路由模式
一般单页面应用是(SPA)不会请求页面而是只更新视图
vue-router提供了两种方式来实现前端路由:Hash模式和History模式,可以用mode参数来决定使用哪一种方式
export default new Router({
mode: 'history',
...
})
hash
vue-router默认使用Hash模式,使用url的hash来模拟一个完整的url
此时url变化时,浏览器是不会重新加载的
Hash(即#)是url的锚点,代表的是网页中的一个位置,仅仅改变#后面部分,浏览器只会滚动对应的位置,而不会重新加载页面
#仅仅只是对浏览器进行指导,而对服务端是完全没有作用的
它不会被包括在http请求中,故也不会重新加载页面。同时hash发生变化时,url都会被浏览器记录下来,这样你就可以使用浏览器的后退了
History
如果你不喜欢hash这种#样式,可以使用history模式
这种模式利用了HTML5 History新增的pushState()和replaceState()方法
除了之前的back,forward,go方法;这两个新方法可以应用在浏览器历史记录的增加替换功能上
使用History模式,通过历史记录修改url。但它不会立即向后端发送请求
注意:虽然History模式可以丢掉不美观的#,也可以正常的前进后退,但是刷新f5后,此时浏览器就会访问服务器,在没有后台支持的情况下,此时就会得到一个404!
官方文档给出的描述是: 这种模式要玩好,还需要后台配置支持
因为我们的应用是单个客户端应用,如果后台没有正确的配置,当用户直接访问时,就会返回404
所以,要在服务端增加一个覆盖所有情况的的候选资源。如果url匹配不到任何静态资源;则应该返回同一个index.html页面
路由属性
路由对象this.$route支持属性如下
-
$route.path字符串,等于当前路由对象的路径,会被解析为绝对路径
/aaa/bbb
-
$route.params包含路由中的动态片段和全匹配片段的键值对,配合动态路由使用
-
$route.query获取连接中查询参数的键值对
/foo?user=1
// 可以获取到
$route.query.user == 1
-
$route.router路由规则所属的路由器以及其所属的组件
-
$route.matched数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象
-
$route.name当前路径的名字
自定义属性
路由中设置自定义参数
router.map({
'/a': {
component: { ... },
auth: true // 这里 auth 是一个自定义字段
}
})
当 /a 被匹配时,$route.auth 的值将会是 true
我们可以利用这个特性在全局的钩子函数中进行身份验证
router.beforeEach(function (transition) {
if (transition.to.auth) {
// 对用户身份进行验证...
}
})
动态路由
:号通配符
路由中的动态片段使用以冒号开头的路径片段定义,类比django中的路由传参
path('<int:name>/',views.xx),
path: '/user/:username',
component: {
template: '<p>用户名是{{$route.params.username}}</p>'
}
一条路径中可以包含多个动态片段,每个片段都会被解析成
$route.params的一个键值对
解析模式
| 模式 | 匹配的路径 | $route.params |
|---|---|---|
/user/:username | /user/evan | { username: 'evan' } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: 123 } |
全匹配路由
*号通配符
动态片段只能匹配路径中的一个部分,而全匹配片段则基本类似于它的贪心版
例如 /foo/*bar 会匹配任何以 /foo/ 开头的路径
当使用一个通配符时,$route.params内会自动添加一个名为pathMatch的参数,其中包含了在连接中通过通配符所匹配到的部分
{
path: '/user/*/cc',
name: 'User',
component: User,
},
访问的URL如下
http://127.0.0.1:8080/user/1231/aaa/cc
那么此时*通配符拿到的部分为
{ "pathMatch": "1231/aaa" }
命名路由
类似django的路由命名,vue路由映射中也可以为某一个路由通过name属性设置命名
{
path: '/user/:userid',
name: 'User',
component: User,
},
在使用v-link标签进行路由跳转时,就可以更加方便啦
<router-link :to="{name: 'User', params: {userid: 'zhangsan' }}">user</router-link>
也可以在js代码中使用router.go切换到该路径下
router.go({ name: 'user', params: { userId: 123 }})
路由跳转
v-link 是一个用来让用户在vue-router应用的不同路径间跳转的指令
该指令接受一个JavaScript表达式,并会在用户点击元素时用该表达式的值去调用 router.go
<!-- 字面量路径 -->
<a v-link="'home'">Home</a>
<router-link :to="{name: 'home'}">Home</router-link>
<!-- 效果同上 -->
<a v-link="{ path: 'home' }">Home</a>
<!-- 具名路径 -->
<a v-link="{ name: 'user', params: { userId: 123 }}">User</a>
一般来说,都应该使用 v-link 而不是 href 来处理浏览时的跳转,这是因为
- 它在
HTML5 history模式和hash模式下的工作方式相同,所以如果你决定改变模式,或者IE9浏览器退化为hash模式时,都不需要做任何改变 - 在
HTML5 history模式下,v-link会监听点击事件,防止浏览器尝试重新加载页面 - 在
HTML5 history模式下使用root选项时,不需要在v-link的URL中包含root路径
其他选项
-
replace一个带有
replace: true的链接被点击时将会触发router.replace()而不是router.go()。由此产生的跳转不会留下历史记录
<router-link :to="{name: 'User', params: {userid: 'zhangsan' }, replace: true }">replace</router-link>
-
append带有
append: true选项的相对路径链接会确保该相对路径始终添加到当前路径之后举例来说,从
/a跳转到相对路径b时,如果没有append: true我们会跳转到/b,但有append: true则会跳转到/a/b
<router-link :to="{name: 'User', append: true }">append</router-link>
路由钩子
- 导航被触发
- 在失活的组件里调用离开守卫
- 调用全局的
beforeEach守卫 - 在重用的组件里调用
beforeRouteUpdate守卫 (2.2+) - 在路由配置里调用
beforeEnter - 解析异步路由组件
- 在被激活的组件里调用
beforeRouteEnter - 调用全局的
beforeResolve守卫 (2.5+) - 导航被确认
- 调用全局的
afterEach钩子 - 触发 DOM 更新
- 用创建好的实例调用
beforeRouteEnter守卫中传给next的回调函数
全局前置钩子
beforeEach(to, from, next)
添加一个全局的前置钩子函数,可以在Router对象初始化时,这样钩子方法会在路由切换开始时调用
调用发生在整个切换流水线之前。如果此钩子函数拒绝了切换,整个切换流水线根本就不会启动,想当于页面不会进行跳转
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
next()
})
export default router
to: 即将要进入的目标 路由对象
from: 当前导航正要离开的路由
next: 实际路由前进的控制方法,调用该方法将执行此次路由跳转,可以传入类似v-link类似的跳转选项参数
如果需要对某些需要用户验证的路由进行身份识别,那么可以像下面这样
router.beforeEach((to, from, next) => {
if(to.meta.requiredAuth == true){ // 如果要进入的路由需要用户登陆
if(window.localStorage.getItem('token')){ // 获取存储的 token
next()
} else{ // 没有获取到,则跳转到登陆页面
next({
path: '/login',
query: toQuery,
})
}
}else{
next()
}
})
全局后置钩子
在完成路由跳转后调用,无next参数改变接下来的前进路由
router.afterEach((to, from) => {
// ...
})
局部路由钩子
定义局部路由钩子,只需要在路由组件映射的位置编写即可,对某个路由映射生效
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
beforeEnter: (to, from, next) => {
// 只对当前路由生效
}
}
]
})
路由元信息
在定义路由时,可以在路由映射中使用meta进行元属性的定义
比如每个页面组件都可以具有不同的title,那么就可以定义在路由中
const router = new VueRouter({
routes: [
{
path: '/index',
component: Index,
meta: {title: index} // 元属性
}
]
})
组件中获取元属性
$route.meta.title
路由钩子中获取元属性
router.beforeEach((to, from, next) => {
const title = to.meta.title;
document.title = title;
next();
})
路由嵌套
当我们发现,编写的组件为一些全局的公共部分,比如后台的左侧导航,除了使用组件封装复用的思路,还可以通过路由嵌套实现代码复用
比如编写一个组件,它具有一个父路由,通过子路由嵌套的方式,可以让所有符合匹配规则的子路由对应的组件渲染在当前组件中
一个导航组件Layout,是这样的
<template>
<section class="app-main">
<div>
这就是导航
</div>
<router-view :key="key" />
</section>
</template>
可以看到,我们使用了router-view标签,这将渲染所有当前组件嵌套路由下的内容
比如当前的导航组件是这样的路由
routes: [
{
path: '/',
component: Layout,
...
}
]
那么嵌套路由可以通过children属性进行定义,未来我们其他需要Layout的组件都可以成为这个路由的子路由
参考官方文档的定义方式,如下所示
routes: [
{
path: '/',
component: Layout,
children: [
{
// 当 /profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
children 配置就是像 routes 配置一样的路由配置数组,所以呢,你可以嵌套多层路由