導(dǎo)航守衛(wèi)
導(dǎo)航守衛(wèi)主要用來通過跳轉(zhuǎn)或取消的方式守衛(wèi)導(dǎo)航。
參數(shù)或查詢的改變并不會(huì)觸發(fā)進(jìn)入/離開的導(dǎo)航守衛(wèi)筝闹。
1、全局守衛(wèi) router.beforeEach
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
????// ...
})
當(dāng)一個(gè)導(dǎo)航觸發(fā)時(shí)腥光,全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用关顷。守衛(wèi)是異步解析執(zhí)行,此時(shí)導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于?等待中武福。
每個(gè)守衛(wèi)方法接收三個(gè)參數(shù):
to: Route: 即將要進(jìn)入的目標(biāo)?路由對(duì)象
from: Route: 當(dāng)前導(dǎo)航正要離開的路由
next: Function: 一定要調(diào)用該方法來?resolve?這個(gè)鉤子议双。執(zhí)行效果依賴?next?方法的調(diào)用參數(shù)
next(): 進(jìn)行管道中的下一個(gè)鉤子。如果全部鉤子執(zhí)行完了捉片,則導(dǎo)航的狀態(tài)就是?confirmed?(確認(rèn)的)平痰。
next(false): 中斷當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了(可能是用戶手動(dòng)或者瀏覽器后退按鈕)界睁,那么 URL 地址會(huì)重置到?from?路由對(duì)應(yīng)的地址觉增。
next('/')?或者?next({ path: '/' }): 跳轉(zhuǎn)到一個(gè)不同的地址。當(dāng)前的導(dǎo)航被中斷翻斟,然后進(jìn)行一個(gè)新的導(dǎo)航逾礁。你可以向?next?傳遞任意位置對(duì)象,且允許設(shè)置諸如?replace: true访惜、name: 'home'?之類的選項(xiàng)以及任何用在?router-link?的?to?prop?或?router.push?中的選項(xiàng)嘹履。
next(error): (2.4.0+) 如果傳入?next?的參數(shù)是一個(gè)?Error?實(shí)例,則導(dǎo)航會(huì)被終止且該錯(cuò)誤會(huì)被傳遞給?router.onError()?注冊(cè)過的回調(diào)债热。
確保要調(diào)用?next?方法砾嫉,否則鉤子就不會(huì)被 resolved。
2窒篱、全局后置鉤子 router.afterEach
后置鉤子和守衛(wèi)不同焕刮,這些鉤子不會(huì)接受?next?函數(shù)也不會(huì)改變導(dǎo)航本身
router.afterEach((to, from) => {
????// ...
})
3舶沿、路由獨(dú)享的守衛(wèi)
在路由配置上直接定義?beforeEnter?守衛(wèi)。
routes: [ {
????path: '/foo',
????component: Foo,????
????beforeEnter: (to, from, next) => {
????????// ...
????}
} ]?
4配并、組件內(nèi)的守衛(wèi)
可以在路由組件內(nèi)直接定義以下路由導(dǎo)航守衛(wèi):
beforeRouteEnter
beforeRouteUpdate?(2.2 新增)
beforeRouteLeave
constFoo = {
????template:`...`,
????beforeRouteEnter (to,from, next) {},
????beforeRouteUpdate (to,from, next) {},
????beforeRouteLeave (to,from, next) {}
}
beforeRouteEnter?守衛(wèi)?不能?訪問?this括荡,因?yàn)槭匦l(wèi)在導(dǎo)航確認(rèn)前被調(diào)用,因此即將登場的新組件還沒被創(chuàng)建。
不過溉旋,你可以通過傳一個(gè)回調(diào)給?next來訪問組件實(shí)例畸冲。在導(dǎo)航被確認(rèn)的時(shí)候執(zhí)行回調(diào),并且把組件實(shí)例作為回調(diào)方法的參數(shù)观腊。
beforeRouteEnter (to, from, next) {
? ? next(vm => {
? ? ????// 通過 `vm` 訪問組件實(shí)例?
????})
}
注意?beforeRouteEnter?是支持給?next?傳遞回調(diào)的唯一守衛(wèi)邑闲。對(duì)于?beforeRouteUpdate?和?beforeRouteLeave?來說,this?已經(jīng)可用了梧油,所以不支持傳遞回調(diào)苫耸,因?yàn)闆]有必要了。
beforeRouteUpdate (to, from, next) {
????// just use `this`
????this.name = to.params.name
????next()
}
這個(gè)離開守衛(wèi)通常用來禁止用戶在還未保存修改前突然離開婶溯。該導(dǎo)航可以通過?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)
? }
}
路由元信息
定義路由的時(shí)候可以配置?meta?字段
{
????path: 'bar',
????component: Bar, // a meta field
????meta: { requiresAuth: true }
}?
我們稱呼?routes?配置中的每個(gè)路由對(duì)象為?路由記錄。
路由記錄可以是嵌套的迄委,因此褐筛,當(dāng)一個(gè)路由匹配成功后,他可能匹配多個(gè)路由記錄叙身。
一個(gè)路由匹配到的所有路由記錄會(huì)暴露為?$route?對(duì)象(還有在導(dǎo)航守衛(wèi)中的路由對(duì)象)的?$route.matched?數(shù)組渔扎。因此,我們需要遍歷?$route.matched?來檢查路由記錄中的?meta?字段信轿。
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() // 確保一定要調(diào)用 next()? }
})
過渡效果
<router-view>是基本的動(dòng)態(tài)組件晃痴,所以我們可以用??<transition> 組件給它添加一些過渡效果
<transition name="slide">
? ? <router-view></router-view>
</transition>
動(dòng)態(tài)過度
<transition :name="transitionName">
? ? <router-view></router-view>
</transition>
// 接著在父組件內(nèi)
// watch $route 決定使用哪種過渡
watch: {
????'$route' (to, from) {
????????const toDepth = to.path.split('/').length
????????const fromDepth = from.path.split('/').length
????????this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
????}
}
數(shù)據(jù)獲取
導(dǎo)航完成之后獲取:先完成導(dǎo)航,然后在接下來的組件生命周期鉤子中獲取數(shù)據(jù)财忽。
導(dǎo)航完成之前獲取:導(dǎo)航完成前倘核,在路由進(jìn)入的守衛(wèi)中獲取數(shù)據(jù),在數(shù)據(jù)獲取成功后執(zhí)行導(dǎo)航即彪。
1紧唱、導(dǎo)航完成之后獲取
當(dāng)使用這種方式時(shí),vue會(huì)馬上導(dǎo)航和渲染組件隶校,然后在組件的?created?鉤子中獲取數(shù)據(jù)漏益。這讓我們有機(jī)會(huì)在數(shù)據(jù)獲取期間展示一個(gè) loading 狀態(tài),還可以在不同視圖間展示不同的 loading 狀態(tài)深胳。
假設(shè)我們有一個(gè)?Post?組件绰疤,需要基于?$route.params.id?獲取文章數(shù)據(jù)
created () {
????this.fetchData()??// 組件創(chuàng)建完后獲取數(shù)據(jù),此時(shí) data 已經(jīng)被 observed 了? ?
},
watch: {
? ? '$route': 'fetchData'? ? // 如果路由有變化舞终,會(huì)再次執(zhí)行該方法? ??
},
methods: {
? ? fetchData () {
? ? ? this.error = this.post = null? ? ?
? ? ? this.loading = true? ? ? ??
? ? ? getPost(this.$route.params.id, (err, post) => {
? ? ? ? ? this.loading = false? ? ? ?
? ? ? ? ? if (err) { this.error = err.toString() } else { this.post = post }
? ? ? })
? ? }
}
2轻庆、在導(dǎo)航完成前獲取數(shù)據(jù)
通過這種方式癣猾,我們?cè)趯?dǎo)航轉(zhuǎn)入新的路由前獲取數(shù)據(jù)。
我們可以在接下來的組件的?beforeRouteEnter?守衛(wèi)中獲取數(shù)據(jù)余爆,當(dāng)數(shù)據(jù)獲取成功后只調(diào)用?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))
? ? })
?},
?// 路由改變前,組件就已經(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í)龙屉,用戶會(huì)停留在當(dāng)前的界面,因此建議在數(shù)據(jù)獲取期間满俗,顯示一些進(jìn)度條或者別的指示转捕。如果數(shù)據(jù)獲取失敗,同樣有必要展示一些全局的錯(cuò)誤提醒唆垃。
滾動(dòng)行為?
使用前端路由五芝,當(dāng)切換到新路由時(shí),實(shí)現(xiàn)切換的頁面滾到頂部辕万,或者是保持原先的滾動(dòng)位置枢步。scrollBehavior
注意: 這個(gè)功能只在支持?history.pushState?的瀏覽器中可。
const router = new VueRouter({
? routes: [...],
? scrollBehavior (to, from, savedPosition) {
? ? // return 期望滾動(dòng)到哪個(gè)的位置?
? }
})
scrollBehavior?方法接收?to?和?from?路由對(duì)象渐尿。第三個(gè)參數(shù)?savedPosition?當(dāng)且僅當(dāng)?popstate?導(dǎo)航 (通過瀏覽器的 前進(jìn)/后退 按鈕觸發(fā)) 時(shí)才可用醉途。
{ x: number, y: number }
{ selector: string, offset? : { x: number, y: number }}?(offset 只在 2.6.0+ 支持)
如果返回一個(gè) falsy (譯者注:falsy 不是?false,參考這里)的值砖茸,或者是一個(gè)空對(duì)象隘擎,那么不會(huì)發(fā)生滾動(dòng)。
舉例:
scrollBehavior (to, from, savedPosition) {
? return { x: 0, y: 0 }
}
返回?savedPosition凉夯,在按下 后退/前進(jìn) 按鈕時(shí)货葬,就會(huì)像瀏覽器的原生表現(xiàn)那樣:
scrollBehavior (to, from, savedPosition) {
? if (savedPosition) {
? ? return savedPosition
? } else {
? ? return { x: 0, y: 0 }
? }
}
模擬『滾動(dòng)到錨點(diǎn)』的行為:
scrollBehavior (to, from, savedPosition) {
? if (to.hash) {
? ? return {
? ? ? selector: to.hash
? ? }
? }
}
異步滾動(dòng)。
返回一個(gè) Promise 來得出預(yù)期的位置描述
scrollBehavior (to, from, savedPosition) {
? return new Promise((resolve, reject) => {
? ? setTimeout(() => {
? ? ? resolve({ x: 0, y: 0 })
? ? }, 500)
? })
}
路由懶加載
當(dāng)打包構(gòu)建應(yīng)用時(shí)劲够,Javascript 包會(huì)變得非常大震桶,影響頁面加載。如果我們能把不同路由對(duì)應(yīng)的組件分割成不同的代碼塊征绎,然后當(dāng)路由被訪問的時(shí)候才加載對(duì)應(yīng)組件蹲姐,這樣就更加高效了。
結(jié)合 Vue 的異步組件和 Webpack 的代碼分割功能炒瘸,輕松實(shí)現(xiàn)路由組件的懶加載淤堵。
首先,可以將異步組件定義為返回一個(gè) Promise 的工廠函數(shù) (該函數(shù)返回的 Promise 應(yīng)該 resolve 組件本身):
constFoo = () =>Promise.resolve({/* 組件定義對(duì)象 */})
第二顷扩,在 Webpack 2 中拐邪,我們可以使用動(dòng)態(tài) import語法來定義代碼分塊點(diǎn) (split point):
import('./Foo.vue')// 返回 Promise
注意:如果您使用的是 Babel,你將需要添加?syntax-dynamic-import?插件隘截,才能使 Babel 可以正確地解析語法扎阶。
結(jié)合這兩者汹胃,這就是如何定義一個(gè)能夠被 Webpack 自動(dòng)代碼分割的異步組件。
constFoo = () =>import('./Foo.vue')
在路由配置中什么都不需要改變东臀,只需要像往常一樣使用?Foo:
constrouter =newVueRouter({? routes: [? ? { path:'/foo', component: Foo }? ]})
把組件按組分塊
有時(shí)候我們想把某個(gè)路由下的所有組件都打包在同個(gè)異步塊 (chunk) 中着饥。只需要使用?命名 chunk,一個(gè)特殊的注釋語法來提供 chunk name (需要 Webpack > 2.4)惰赋。
constFoo = () =>import(/* webpackChunkName: "group-foo" */'./Foo.vue')
constBar = () =>import(/* webpackChunkName: "group-foo" */'./Bar.vue')
constBaz = () =>import(/* webpackChunkName: "group-foo" */'./Baz.vue')
Webpack 會(huì)將任何一個(gè)異步模塊與相同的塊名稱組合到相同的異步塊中宰掉。