有道云笔记:http://note.youdao.com/s/QPUDHXcm
Vue-Router
基础
官方文档:https://router.vuejs.org/zh/guide/
进阶
导航守卫
解析流程
- 导航被触发
- 在失活组件里调用 beforeRouteLeave守卫 调用全局的前置 befoeEach
- (在复用组件里调用 beforeRouteUpdate守卫)
- 在路由配置里调用 beforeEnter
- 解析异步路由组件
- 在被激活组件里调用 beforeRouteEnter守卫
- 调用全局解析 beforeResolve
- 导航被确认
- 调用全局的后置 afterEach
- 触发DOM更新
- 调用beforeRouteEnter守卫中传给 next 的回调函数,创建好的组件
- 实例会作为回调函数得到参数传入
1、全局前置守卫 beforeEach
当一个导航触发时,全局前置守卫 按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
const router = new VueRouter({...})
router.beforeEach((to, from, next) => {
// ...
})
参数:
- to:即将要进入的目标 路由对象
- from:当前导航正要离开的路由
- next:Function:一定要调用该方法来 resilve 这个钩子。
next() 进行管道中的下一个钩子。
next(false) 中断当前的导航。
next(’/’) 或者 next( { path:’/’ } ) 跳转到一个不同的地址。当前导航被中断,然后进行一个新的导航 next(error)
例子
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
2、全局解析守卫 beforeResolve
和 router.beforeEach 类似。区别是 在导航确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
验证例子:
router.beforeEach((to, from, next) => {
console.log('before each invoked');
if (to.fullPath === '/app') {
// next('/login')//不仅可以写字符串还可以是一个对象
next({path: '/login', replace})
} else {
next()
}
});
router.beforeResolve((to, from, next) => {
console.log("before resolve invoked")
next();
});
router.afterEach((to, from) =>{
console.log("after invoked")
})
3、全局后置钩子 afterEach
router.afterEach((to, from) => {
// ...
})
4、路由独享的守卫 beforeEnter
在路由配置上直接定义
const router = new VueRouter({
routes: [{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}]
})
5、组件内的守卫 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
//在渲染该组件的对应路由被 confirm(确认) 前调用
//不能!获取组件实例 'this'
//因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
//在当前路由改变,但是该组件被复用时调用
//举例,对于一个带有动态参数的路径,/foo/:id,在/foo/1 和 /foo/2 之间跳转时
//由于会渲染同样的Foo组件,因此组件实例会被复用,而这个钩子就会在这个情况下被调用
//可以访问组件实例'this'
},
beforeRouteLeave (to, from, next) {
//导航离开该组件的对应路由时调用
//可以访问组件实例'this'
}
}
如果 beforeRouteEnter守卫 要访问组件实例,可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
这个 beforeRouteLeave守卫 通常用来禁止用户在还未保存修改前突然离开,该导航可以通过 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 }
}
]
}
]
})
// 下面例子展示在全局导航守卫中检查元字段:
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()
}
})
实例:参考:https://www.cnblogs.com/-xiao/p/11126064.html
//route
meta: { title:'关于' }
// 获取数据的两种方式:
// 1、
$route.meta.xxxx
//2、路由前置守卫router.beforeEach 中获取 meta 中的 title 数据,并设置为页面标题
router.beforeEach((to,from,next)=>{
// && 与运算 有时可以代替用来代替if 如果前一个值为 true ,则返回后面那个值
to.meta && setTitle(to.meta.title)
})
export const setTitle = (title) => {
// 如果该路由没有设置title值,则前一个是 undefined || 前一个值为false时直接返回admin
// console.log(window.document);
window.document.title = title || 'admin'
}
获取数据
假设我们有一个 Post 组件,需要基于 $route.params.id 获取文章数据
<template>
<div class="post">
<div v-if="loading" class="loading">
Loading...
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
1、导航完成后获取数据
这种方式会马上导航 和 渲染组件,然后在组件的 created 钩子 中获取数据。
这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 组件创建完后获取数据,
// 此时 data 已经被 observed 了
this.fetchData()
},
watch: {
// 如果路由有变化,会再次执行该方法
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
2、导航完成前获取数据
这种方式在导航转入新的路由前获取数据。可以在接下来的组件的 beforeRouteEnter守卫 中获取数据,当数据获取成功后只调用 next 方法。
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
}
}
}
}
在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。如果数据获取失败,同样有必要展示一些全局的错误提醒。
滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。
// 当创建一个 Router 实例,你可以提供一个 scrollBehavior 方法:
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
详情看官网:https://router.vuejs.org/zh/guide/advanced/scroll-behavior.html#%E5%BC%82%E6%AD%A5%E6%BB%9A%E5%8A%A8
路由懒加载
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})