聊完了基礎的路由功能後,我們接著來看看關於路由的進階應用方式。你放心,前陣子一直提到的動態元件載入,這邊一樣會出現,所以先給各位打個預防針。不過,如果你真的看膩了什麼動態載入的事情,那麼你想要跳過我也是欣然接受。
何況,進來就進來,出去就出去, 哪有人一直動態載入的啦。
在 Router 當中提供了幾種所謂的路由守衛方法:
其實我覺得叫路由防衛比較好(聽起來超中二的)
beforeEach
每個路由要被執行之前,都會先經過這裡,他會傳入三個參數:
to
你要去的路由位置。from
你從哪一個路由位置進來,如果沒有,預設是 null
。next()
繼續往下執行的回呼函式,你必須要呼叫他才能繼續執行。beforeResolve
當路由與所搭配的元件都被解析後,會執行這個函式,傳入參數跟 beforeEach
一樣,功能也一樣。beforeEnter
用於 Route 設定的地方,傳入參數跟 beforeEach
一樣,功能也一樣。首先我們說明一下 next()
這個回呼函式,基本上無論你在哪一個階段,只要 next()
沒有被呼叫到,那麼你的路由基本上就會被中斷。而這個 next()
可以接受的呼叫方法有:
next()
就這樣,很單純的呼叫。next(false)
代表中斷這個路由執行,畫面就會停住了。next({ name: 'HelloKitty' })
代表前往路由名稱為 HelloKitty 的地方。這種呼叫方式可以套用 <router-link>
的 to
的屬性,可以參考我昨天的文章 Router 基本入門 Day 9。next(Error)
傳入一個 Error 的實例,該路由會被中斷,然後發一個事件給 router.onError()
。至於剛剛說的 beforeEnter
的使用方式,是在 Route 設定的時候:
const router = new Router({
mode: 'history',
routes: [
{
path: '/helloworld',
component: HelloWorld,
beforeEnter (to, from, next) {
next();
}
}
]
});
然後在元件裡面,當你使用了 Vue Router 的時候,在整個生命週期當中,也提供了三組方法可以使用:
beforeRouteEnter
路由尚未進入該元件時會執行,一樣會三個傳入三個參數:
to
你要去的路由位置。from
你從哪一個路由位置進來,如果沒有,預設是 null
。next()
繼續往下執行的回呼函式,你必須要呼叫他才能繼續執行。beforeRouteUpdate
當此元件被重複使用時,路由有更新,會呼叫這個方法,參數跟 beforeRouteEnter
相同。beforeRouteLeave
當路由要離開該元件時,會呼叫這個方法,參數跟 beforeRouteEnter
相同。以上三組沒有太大的區別,唯一有差異的是 beforeRouteEnter
,由於他是在路由進入點之前,也就是說在路由尚未進入,所以他沒有 this
可以用,而其他兩個,在方法中的 this
均指向元件實體。
而 beforeRouteEnter
若要拿到元件實體,必須使用 next()
來實作:
export default {
name: 'HelloWorld',
beforeRouteEnter (to, from, next) {
next(vm => {
// 這裡的 vm 就是 HelloWorld 這個元件的實體。
})
}
}
或許你會問說,這些東西到底有什麼好處?為何要叫做 路由守衛 ?我們底下舉出一些例子,你思考一下當你要做這些事情的時候,你會怎麼做?
我們以登入檢查為例子,如果你沒有這些方法,那麼你勢必要在每個路徑所經過的地方,都要綁上一個檢查的步驟。這樣其實在後續維護上就會變得相當棘手。而,beforeEach
,beforeResolve
,beforeEnter
或是 beforeRouteEnter
就可以幫助你做掉這件事情。
詳細的流程可以參考官方說明:
當然,你可能不會想放在 beforeRouteEnter
,這樣等於每個元件都要做。所以,我們以 beforeEnter
為例子,就可以這麼做:
const router = new Router({
mode: 'history',
routes: [
{
path: '/need-to-login',
component: HelloWorld,
beforeEnter (to, from, next) {
checkLoginStatus()
.then(next)
.catch(() => {
next({ name: 'signin' })
});
}
}
]
});
倘若你的 need-to-login
底下還有 children
的話,也不用擔心,因為他一定會執行。即便你是直接進入其下的路由設定,他的上游的動作還是會被執行的。
不過,請注意一點,由於路由執行的順序,會先走過一次 beforeRouteLeave
,所以,如果你在這邊有做什麼 登出 的動作,那麼你可能每次登入,換頁後就會一直被彈到登入頁面。
另外一個方式,是可以放在最外層的 beforeEach
的地方,作法大同小異,但為了識別 Route,所以你可以使用 meta
來儲存一些識別用資料,舉例來說:
const router = new Router({
mode: 'history',
routes: [
{
path: '/need-to-login',
component: HelloWorld,
meta: {
needLogin: true
}
}
]
});
router.beforeEach(function(to, from, next) {
if (to.meta.needLogin === true) {
return checkLoginStatus()
.then(next)
.catch(() => {
next({ name: 'signin' })
});
}
next();
});
但是!要注意一點,由於 beforeEach
並不會幫你做麼上游的動作,他是 每一個 路由進入之前會做一些事情,所以當你的 need-to-login
其下有子路由,那麼也一定要加上相同的 meta
來儲存識別資料,否則,你就會跑出一個漏洞了。
既然,這個方法可以 阻擋 路由實例的流程,那麼我們在阻擋的同時,是不是可以拿來做其他的事情?當然,可以,不然我就不會又跳出來講動態載入了。
如果聽膩的人左轉沒關係,謝謝。
這裡我有寫一點邪魔歪道 心臟比較大顆的可以試試看。
是的,就如同之前提到的 import
,Vue Router 本身也支援懶加載這件事情,所以,我們在載入元件的時候,可以這麼做:
const HelloWorld = () => import('@/components/HelloWorld.vue')
const router = new Router({
mode: 'history',
routes: [
{
path: '/helloworld',
component: HelloWorld,
beforeEnter (to, from, next) {
next();
}
}
]
});
你在開發模式其實不會有感覺,當你運行 yarn build
將正式環境打包後,你會發現你使用 import
的一些模組,會被分割成不同的 .js
檔案。而這些檔案會在 需要的時候 才會被載入到前端,用以實現懶加載這件事情。
當然,這也是相當好用的方法。不過,今天如果我連 元件本身 都還不確定存不存在呢?
請回想一下 Component 魔術方法 Day 5
所以,我們可以這麼做:
component
。Vue.options
來配發 component
。component
。有沒有發現,其實到最後都是一種 邪魔歪道 的風格。
沒辦法,我長得不正。
我這邊不會在提 XHR, Promise 與 Vue.options
怎麼做了,我們就來講一個,利用 JavaScript 語言本身的特(缺)性(陷),來更改 components
。我這邊先上 Code 與執行結果,但是不要問我為什麼會這樣!
import Router from 'vue-router';
import HelloKitty from '@/components/HelloKitty.vue';
const router = new Router({
mode: 'history',
routes: [
{
path: '/helloworld',
component: null,
beforeEnter (to, from, next) {
to.matched[0].components.default = HelloKitty;
next();
}
}
]
});
關於為何會這樣,可以參考 Kuro 講解 JavaScript 的文章。
說句實在話,你是不是覺得 JavaScript 好棒棒!
我這邊就大幅縮減動態載入的事情跟範例,反正我們之後還會繼續提到(啊哈哈哈哈)。關於路由的使用大概是這樣,如果你沒有要做什麼奇怪的事情,那麼基本上不太會用到更深的東西了。
官方文件 還是要參考一下!