导航守卫
完整的导航解析流程
- 导航被触发
- 在失活的组件里调用离开守卫
beforeRouteLeave
- 调用全局
beforeEach
守卫 - 调用重用组件中
beforeRouteUpdate
守卫(2.2+) - 调用路由配置里
beforeEnter
守卫 - 解析异步路由组件
- 在被激活的组件里调用
beforeRouteEnter
守卫 - 调用全局的
beforeResolve
守卫(2.5+) - 导航被确认
- 调用全局的
afterEach
钩子 - 触发DOM更新
- 用创建好的实例调用
beforeRouteEnter
守卫中传给next
的回调函数
全局守卫
const router = new VueRouter({...});
router.beforeEach(function (to,from,next) {})
-
to
是即将进入的路由对象,$route
-
from
是正要离开的路由 -
next
方法
-
next()
进入管道中下一个守卫,如果执行完了,就是导航确认 -
next(false)
中端当前的导航 -
next('/')
跳转到另一个地址 -
next(error)
,当error是Error实例时,触发router.onError()
全局解析守卫
调用的时机不同,在所有组件内守卫和异步路由组件被解析之后
组件守卫
const foo = {
template: '',
beforeRouteEnter: function (to,from,next) {
},
beforeRouteUpdate: function (to,from,next) {
},
beforeRoteLeave: function (to,from,next) {
}
}
-
beforeRouteEnter
中不能访问this
- 传递一个回调给
next
,在导航确认的时候执行,通过vm
实例访问组件
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
路由独享的守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to,from,next) => {
// ...
}
}
]
})
路由元信息
为路由添加路由元信息,案例中可以使用它来判断,该页面是否需要身份确认
-
/foo/bar
将会匹配父路由记录和子路由记录,在$route.matched
数组中可以查询到 - 不仅组件中
$route
是路由信息对象,各个守卫中to
也是路由信息对象 - 在各个守卫中,可以查看匹配的路由的
meta
,确认登陆该路由是否需要特殊的条件,比如需要登陆状态才可以访问
// 设置 路由元 信息,让导航到该连接的时候进行身份确认
const router = new VueRouter({
routes: [
{
path: '/foo',
compoent: Foo,
children: [
{
path: 'bar',
component: Bar,
meta: { requiresAuth: true }
}
]
}
]
});
// 路由全局注册,当跳转时,确认用户是否跳转
// to 是将要到达的路由对象,其中`$route.matched`存储着匹配的所有路由记录
router.beforeEnter((to,from,next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!auth.loggedIn()) {
next({
path: '/login',
query: {
redirect: to.fullPath
}
});
}else {
next();
}
}else {
next();
}
});
过渡效果
为
router-view
添加过渡效果
<transition>
<router-view></router-view>
</transition>
单个路由过渡效果,在每个路由对应的模板内添加transition。这样,每个路由都有自己的过渡效果
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}
重点。根据路由路径的不同,通过动态绑定,添加不同的过渡效果
<transition :name="transitionName">
<router-view></router-view>
</transition>
// 父组件中监听 $route
watch: {
'$route' (to,from) {
const toDepth = to.path.split('.').length;
const fromDepth = from.path.split('.').length;
this.trasitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
数据获取
数据获取可以下导航完成之前,也可以在导航完成之后
- 关于导航时,数据获取,有一个状态
loading
导航完成后获取数据
// 通过监听路由的变化,获取数据
export default {
created: function () {
// 组件钩子函数,当组件创建完成之后
this.fetchData();
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// 下边是一个自定义获取数据的方法,可以自己写
getPost(this.$route.param.id,(err,post)=>{
// 数据拿回来之后可以不是loading状态了
this.loading = false;
if (err) {
this.error = err.toString()
} else {
this.post = post
}
});
}
}
}
在导航前获取数据
// 根据组件的钩子函数
export default {
data () {
return {
post: null,
error: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
建议,在获取数据的时候可以进行进度条显示。如果数据获取失败,要展示一些全局的错误提醒。
滚动行为
这个行为只有在html5 History模式下才可以用,当跳转页面的时候,页面滚动某处
const router = new VueRouter({
routes,
scrollBehavior: function (to,from,savedPosition) {
// 第三个参数只有当使用浏览器的前进后退键时,才有
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
// 或者跳转到锚点
return {
selector: to.hash
}
}
}
})