導(dǎo)航守衛(wèi)
完整的導(dǎo)航解析流程
- 導(dǎo)航被觸發(fā)
- 在失活的組件里調(diào)用離開守衛(wèi)
beforeRouteLeave - 調(diào)用全局
beforeEach守衛(wèi) - 調(diào)用重用組件中
beforeRouteUpdate守衛(wèi)(2.2+) - 調(diào)用路由配置里
beforeEnter守衛(wèi) - 解析異步路由組件
- 在被激活的組件里調(diào)用
beforeRouteEnter守衛(wèi) - 調(diào)用全局的
beforeResolve守衛(wèi)(2.5+) - 導(dǎo)航被確認
- 調(diào)用全局的
afterEach鉤子 - 觸發(fā)DOM更新
- 用創(chuàng)建好的實例調(diào)用
beforeRouteEnter守衛(wèi)中傳給next的回調(diào)函數(shù)
全局守衛(wèi)
const router = new VueRouter({...});
router.beforeEach(function (to,from,next) {})
-
to是即將進入的路由對象,$route -
from是正要離開的路由 -
next方法
-
next()進入管道中下一個守衛(wèi),如果執(zhí)行完了,就是導(dǎo)航確認 -
next(false)中端當前的導(dǎo)航 -
next('/')跳轉(zhuǎn)到另一個地址 -
next(error),當error是Error實例時,觸發(fā)router.onError()
全局解析守衛(wèi)
調(diào)用的時機不同,在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后
組件守衛(wèi)
const foo = {
template: '',
beforeRouteEnter: function (to,from,next) {
},
beforeRouteUpdate: function (to,from,next) {
},
beforeRoteLeave: function (to,from,next) {
}
}
-
beforeRouteEnter中不能訪問this - 傳遞一個回調(diào)給
next,在導(dǎo)航確認的時候執(zhí)行,通過vm實例訪問組件
beforeRouteEnter (to, from, next) {
next(vm => {
// 通過 `vm` 訪問組件實例
})
}
路由獨享的守衛(wèi)
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to,from,next) => {
// ...
}
}
]
})
路由元信息
為路由添加路由元信息,案例中可以使用它來判斷,該頁面是否需要身份確認
-
/foo/bar將會匹配父路由記錄和子路由記錄,在$route.matched數(shù)組中可以查詢到 - 不僅組件中
$route是路由信息對象,各個守衛(wèi)中to也是路由信息對象 - 在各個守衛(wèi)中,可以查看匹配的路由的
meta,確認登陸該路由是否需要特殊的條件,比如需要登陸狀態(tài)才可以訪問
// 設(shè)置 路由元 信息,讓導(dǎo)航到該連接的時候進行身份確認
const router = new VueRouter({
routes: [
{
path: '/foo',
compoent: Foo,
children: [
{
path: 'bar',
component: Bar,
meta: { requiresAuth: true }
}
]
}
]
});
// 路由全局注冊,當跳轉(zhuǎn)時,確認用戶是否跳轉(zhuǎn)
// 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>
單個路由過渡效果,在每個路由對應(yīng)的模板內(nèi)添加transition。這樣,每個路由都有自己的過渡效果
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}
重點。根據(jù)路由路徑的不同,通過動態(tài)綁定,添加不同的過渡效果
<transition :name="transitionName">
<router-view></router-view>
</transition>
// 父組件中監(jiān)聽 $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'
}
}
數(shù)據(jù)獲取
數(shù)據(jù)獲取可以下導(dǎo)航完成之前,也可以在導(dǎo)航完成之后
- 關(guān)于導(dǎo)航時,數(shù)據(jù)獲取,有一個狀態(tài)
loading
導(dǎo)航完成后獲取數(shù)據(jù)
// 通過監(jiān)聽路由的變化,獲取數(shù)據(jù)
export default {
created: function () {
// 組件鉤子函數(shù),當組件創(chuàng)建完成之后
this.fetchData();
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// 下邊是一個自定義獲取數(shù)據(jù)的方法,可以自己寫
getPost(this.$route.param.id,(err,post)=>{
// 數(shù)據(jù)拿回來之后可以不是loading狀態(tài)了
this.loading = false;
if (err) {
this.error = err.toString()
} else {
this.post = post
}
});
}
}
}
在導(dǎo)航前獲取數(shù)據(jù)
// 根據(jù)組件的鉤子函數(shù)
export default {
data () {
return {
post: null,
error: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改變前,組件就已經(jīng)渲染完了
// 邏輯稍稍不同
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
}
}
}
}
建議,在獲取數(shù)據(jù)的時候可以進行進度條顯示。如果數(shù)據(jù)獲取失敗,要展示一些全局的錯誤提醒。
滾動行為
這個行為只有在html5 History模式下才可以用,當跳轉(zhuǎn)頁面的時候,頁面滾動某處
const router = new VueRouter({
routes,
scrollBehavior: function (to,from,savedPosition) {
// 第三個參數(shù)只有當使用瀏覽器的前進后退鍵時,才有
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
// 或者跳轉(zhuǎn)到錨點
return {
selector: to.hash
}
}
}
})