1.導航守衛
正如其名,vue-router
提供的導航守衛主要用來通過跳轉或取消的方式守衛導航。有多種機會植入路由導航過程中:全局的, 單個路由獨享的, 或者組件級的。
記住參數或查詢的改變並不會觸發進入/離開的導航守衛。你可以通過觀察 $route
對象來應對這些變化,或使用 beforeRouteUpdate
的組件內守衛
全局前置守衛
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
當一個導航觸發時,全局前置守衛按照創建順序調用。守衛是異步解析執行,此時導航在所有守衛 resolve 完之前一直處於 等待中
每個守衛方法接收三個參數:
to: Route
: 即將要進入的目標 路由對象from: Route
: 當前導航正要離開的路由next: Function
: 一定要調用該方法來 resolve 這個鈎子。執行效果依賴 next 方法的調用參數。next()
: 進行管道中的下一個鈎子。如果全部鈎子執行完了,則導航的狀態就是 confirmed (確認的)next(false)
: 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址。next('/')
或者next({ path: '/' })
: 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。你可以向 next 傳遞任意位置對象,且允許設置諸如replace: true
、name:
'home'
之類的選項以及任何用在router-link
的to
prop 或router.push
中的選項。next(error)
: (2.4.0+) 如果傳入 next 的參數是一個Error
實例,則導航會被終止且該錯誤會被傳遞給router.onError()
注冊過的回調。
確保要調用 next 方法,否則鈎子就不會被 resolved
舉個例子
router.beforeEach((to, from, next) => {
if(to.name === 'One'){
next('Two')
}
next()
})
當我們訪問
http://localhost:8080/#/One
時,就會跳到
http://localhost:8080/#/Two
全局解析守衛
在 2.5.0+ 你可以用 router.beforeResolve 注冊一個全局守衛。這和 router.beforeEach 類似,區別是在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用。
router.beforeEach((to, from, next) => {
if(to.name === 'One'){
next('Two')
}
next()
})
router.beforeResolve((to, from, next) => {
if(to.name === 'Two'){
next('Three')
}
next()
})
當我們訪問
http://localhost:8080/#/One
時,就會跳到
http://localhost:8080/#/Two
然后再跳到
http://localhost:8080/#/Three
全局后置鈎子
你也可以注冊全局后置鈎子,然而和守衛不同的是,這些鈎子不會接受 next 函數也不會改變導航本身
router.afterEach((to, from) => {
// ...
})
路由獨享的守衛
路由
{
path: '/One',
name: 'One',
component: One,
beforeEnter: (to, from, next) => {
if(to.name === 'One'){
next('Two')
}
next()
}
}
當我們訪問
http://localhost:8080/#/One
時,就會跳到
http://localhost:8080/#/Two
組件內的守衛
- beforeRouteEnter
- beforeRouteUpdate (2.2 新增)
- beforeRouteLeave
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應路由被 confirm 前調用
// 不!能!獲取組件實例 `this`
// 因為當守衛執行前,組件實例還沒被創建
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,但是該組件被復用時調用
// 舉例來說,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 由於會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鈎子就會在這個情況下被調用。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導航離開該組件的對應路由時調用
// 可以訪問組件實例 `this`
}
beforeRouteEnter
守衛 不能 訪問 this
,因為守衛在導航確認前被調用,因此即將登場的新組件還沒被創建。
不過,你可以通過傳一個回調給 next
來訪問組件實例。在導航被確認的時候執行回調,並且把組件實例作為回調方法的參數。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通過 `vm` 訪問組件實例
})
}
注意 beforeRouteEnter
是支持給 next
傳遞回調的唯一守衛。對於 beforeRouteUpdate
和 beforeRouteLeave
來說,this
已經可用了,所以不支持傳遞回調,因為沒有必要了
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
beforeRouteLeave
beforeRouteLeave (to, from , next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
這個離開守衛通常用來禁止用戶在還未保存修改前突然離開。該導航可以通過 next(false) 來取消
完整的導航解析流程!!!
- 導航被觸發。
- 在失活的組件里調用離開守衛。
- 調用全局的
beforeEach
守衛。 - 在重用的組件里調用
beforeRouteUpdate
守衛 (2.2+)。 - 在路由配置里調用
beforeEnter
。 - 解析異步路由組件。
- 在被激活的組件里調用
beforeRouteEnter
。 - 調用全局的
beforeResolve
守衛 (2.5+)。 - 導航被確認。
- 調用全局的
afterEach
鈎子。 - 觸發 DOM 更新。
- 用創建好的實例調用
beforeRouteEnter
守衛中傳給next
的回調函數。
2.路由元信息
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
meta: { requiresAuth: true }
}
]
}
]
})
我們稱 routes
配置中的每個路由對象為一個路由記錄。路由記錄可以是嵌套的,因此,當一個路由匹配成功后,他可能匹配多個路由記錄
一個路由匹配到的所有路由記錄會暴露為 $route
對象 (還有在導航守衛中的路由對象) 的 $route.matched
數組。因此,我們需要遍歷 $route.matched
來檢查路由記錄中的 meta
字段。
在舉個栗子
router.beforeEach((to, from, next) => {
//判斷路由記錄是否需要驗證登錄
if (to.matched.some(record => record.meta.requiresAuth)) {
// 自己定義的判斷登錄的方法
let isLogin = getLoginStatus()
if (!isLogin()) {
next({
// 跳轉到登錄頁
path: '/login',
// 登錄頁需要知道從哪跳過來的,方便登錄成功后回到原頁面
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 確保一定要調用 next()
}
})
注意!!!
next一定要執行不然鈎子函數不會resolved。
3.滾動行為
使用前端路由,當切換到新路由時,想要頁面滾到頂部,或者是保持原先的滾動位置,就像重新加載頁面那樣。 vue-router 能做到,而且更好,它讓你可以自定義路由切換時頁面如何滾動。
注意: 這個功能只在支持 history.pushState 的瀏覽器中可用。
當創建一個 Router 實例,你可以提供一個 scrollBehavior 方法
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
if(to.name === 'BookDetails'){
return { x: 0, y: 0 }
}
}
})
scrollBehavior
方法接收 to
和 from
路由對象。第三個參數 savedPosition
當且僅當 popstate
導航 (通過瀏覽器的 前進/后退 按鈕觸發) 時才可用
當我們訪問BookDetails路由的時候,就會回到頂部
當然還有好多,就不一一介紹了,可以看這里
4.路由懶加載
懶加載也叫延遲加載,即在需要的時候進行加載,隨用隨載。在單頁應用中,如果沒有應用懶加載,運用webpack打包后的文件將會異常的大,造成進入首頁時,需要加載的內容過多,延時過長,不利於用戶體驗,而運用懶加載則可以將頁面進行划分,需要的時候加載頁面,可以有效的分擔首頁所承擔的加載壓力,減少首頁加載用時。
按需加載
{
path: '/Fruits',
name: 'Fruits',
component: require('@/components/Fruits') // 非按需加載
}
{
path: '/Fruits',
name: 'Fruits',
component: require(['@/components/Fruits'], resolve) // 按需加載
}
按需加載會在頁面第一次請求的時候,把相關路由組件塊的js添加上;非按需加載則會把所有的路由組件塊的js包打在一起
import()
- 推薦使用這種方式(需要webpack > 2.4)
- webpack官方文檔:webpack中使用import() vue官方文檔:路由懶加載使用import()
const Fruits = () => import('@/components/Fruits')
{
path: '/Fruits',
name: 'Fruits',
component: Fruits
}
或者
{
path: '/Fruits',
name: 'Fruits',
component: () => import('@/components/Fruits')
}
把組件按組分塊
有時候我們想把某個路由下的所有組件都打包在同個異步塊 (chunk) 中。只需要使用 命名 chunk,一個特殊的注釋語法來提供 chunk name (需要 Webpack > 2.4)
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
Webpack 會將任何一個異步模塊與相同的塊名稱組合到相同的異步塊中