說到Vue的鉤子函數,可能不少人只停留在一些很簡單經常使用的鉤子(created
,mounted
),並且對於裏面的區別,何時該用什麼鉤子,並無仔細的去研究過,且Vue的生命週期在面試中也算是比較高頻的考點,那麼該如何回答這類問題,讓人有眼前一亮的感受呢...html
有的時候,咱們須要經過路由來進行一些操做,好比最多見的登陸權限驗證,當用戶知足條件時,才讓其進入導航,不然就取消跳轉,並跳到登陸頁面讓其登陸。vue
爲此咱們有不少種方法能夠植入路由的導航過程:全局的, 單個路由獨享的, 或者組件級的,推薦優先閱讀路由文檔git
vue-router全局有三個守衛:github
使用方法:web
// main.js 入口文件
import router from './router'; // 引入路由
router.beforeEach((to, from, next) => {
next();
});
router.beforeResolve((to, from, next) => {
next();
});
router.afterEach((to, from) => {
console.log('afterEach 全局後置鉤子');
});
複製代碼
to和from是將要進入和將要離開的路由對象,路由對象指的是平時經過this.$route獲取到的路由對象。面試
next:Function 這個參數是個函數,且必須調用,不然不能進入路由(頁面空白)。ajax
next() 進入該路由。正則表達式
next(false): 取消進入路由,url地址重置爲from路由地址(也就是將要離開的路由地址)。vue-router
next 跳轉新路由,當前的導航被中斷,從新開始一個新的導航。
咱們能夠這樣跳轉:next('path地址')或者next({path:''})或者next({name:''})
且容許設置諸如 replace: true、name: 'home' 之類的選項
以及你用在router-link或router.push的對象選項。
複製代碼
若是你不想全局配置守衛的話,你能夠爲某些路由單獨配置守衛:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// 參數用法什麼的都同樣,調用順序在全局前置守衛後面,因此不會被全局守衛覆蓋
// ...
}
}
]
})
複製代碼
文檔中的介紹:
beforeRouteEnter (to, from, next) {
// 在路由獨享守衛後調用 不!能!獲取組件實例 `this`,組件實例還沒被建立
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,可是該組件被複用時調用 能夠訪問組件實例 `this`
// 舉例來講,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 因爲會渲染一樣的 Foo 組件,所以組件實例會被複用。而這個鉤子就會在這個狀況下被調用。
},
beforeRouteLeave (to, from, next) {
// 導航離開該組件的對應路由時調用,能夠訪問組件實例 `this`
}
複製代碼
beforeRouteEnter訪問this
由於鉤子在組件實例還沒被建立的時候調用,因此不能獲取組件實例 this
,能夠經過傳一個回調給next
來訪問組件實例 。
可是回調的執行時機在mounted後面,因此在我看來這裏對this的訪問意義不太大,能夠放在created
或者mounted
裏面。
beforeRouteEnter (to, from, next) {
console.log('在路由獨享守衛後調用');
next(vm => {
// 經過 `vm` 訪問組件實例`this` 執行回調的時機在mounted後面,
})
}
複製代碼
beforeRouteLeave:
導航離開該組件的對應路由時調用,咱們用它來禁止用戶離開,好比還未保存草稿,或者在用戶離開前,將setInterval
銷燬,防止離開以後,定時器還在調用。
beforeRouteLeave (to, from , next) {
if (文章保存) {
next(); // 容許離開或者能夠跳到別的路由 上面講過了
} else {
next(false); // 取消離開
}
}
複製代碼
若是咱們在全局守衛/路由獨享守衛/組件路由守衛的鉤子函數中有錯誤,能夠這樣捕獲:
router.onError(callback => {
// 2.4.0新增 並不經常使用,瞭解一下就能夠了
console.log(callback, 'callback');
});
複製代碼
在路由文檔中還有更多的實例方法:動態添加路由等,有興趣能夠了解一下。
我瞭解到的,不少人會碰到這個問題,來看一下這段僞代碼:
router.beforeEach((to, from, next) => {
if(登陸){
next()
}else{
next({ name: 'login' });
}
});
複製代碼
看邏輯貌似是對的,可是當咱們跳轉到login
以後,由於此時仍是未登陸狀態,因此會一直跳轉到login
而後死循環,頁面一直是空白的,因此:咱們須要把判斷條件稍微改一下。
if(登陸 || to.name === 'login'){ next() } // 登陸,或者將要前往login頁面的時候,就容許進入路由
複製代碼
文檔中提到由於router.afterEach不接受next
函數因此也不會改變導航自己,意思就是隻能當成一個鉤子來使用,可是我本身在試的時候發現,咱們能夠經過這種形式來實現跳轉:
// main.js 入口文件
import router from './router'; // 引入路由
router.afterEach((to, from) => {
if (未登陸 && to.name !== 'login') {
router.push({ name: 'login' }); // 跳轉login
}
});
複製代碼
額,經過router.beforeEach 也徹底能夠實現且更好,我就騷一下。
beforeRouteLeave
beforeEach
beforeRouteUpdate
beforeEnter
。beforeRouteEnter
beforeResolve
afterEach
鉤子。mounted
)。beforeRouteEnter
守衛中傳給 next 的回調函數在開發Vue項目的時候,大部分組件是不必屢次渲染的,因此Vue提供了一個內置組件keep-alive
來緩存組件內部狀態,避免從新渲染,文檔在這裏。
文檔:和
<transition>
類似,<keep-alive>
是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出如今父組件鏈中。
緩存動態組件:
<keep-alive>
包裹動態組件時,會緩存不活動的組件實例,而不是銷燬它們,此種方式並沒有太大的實用意義。
<!-- 基本 -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<!-- 多個條件判斷的子組件 -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive>
複製代碼
緩存路由組件:
使用keep-alive
能夠將全部路徑匹配到的路由組件都緩存起來,包括路由組件裏面的組件,keep-alive
大多數使用場景就是這種。
<keep-alive>
<router-view></router-view>
</keep-alive>
複製代碼
這篇既然是Vue鉤子函數的專場,那確定要扣題呀~
在被keep-alive
包含的組件/路由中,會多出兩個生命週期的鉤子:activated
與 deactivated
。
文檔:在 2.2.0 及其更高版本中,activated 和 deactivated 將會在 樹內的全部嵌套組件中觸發。
activated在組件第一次渲染時會被調用,以後在每次緩存組件被激活時調用。
activated調用時機:
第一次進入緩存路由/組件,在mounted
後面,beforeRouteEnter
守衛傳給 next 的回調函數以前調用:
beforeMount=> 若是你是從別的路由/組件進來(組件銷燬destroyed/或離開緩存deactivated)=>
mounted=> activated 進入緩存組件 => 執行 beforeRouteEnter回調
複製代碼
由於組件被緩存了,再次進入緩存路由/組件時,不會觸發這些鉤子:
// beforeCreate created beforeMount mounted 都不會觸發。
複製代碼
因此以後的調用時機是:
組件銷燬destroyed/或離開緩存deactivated => activated 進入當前緩存組件
=> 執行 beforeRouteEnter回調
// 組件緩存或銷燬,嵌套組件的銷燬和緩存也在這裏觸發
複製代碼
deactivated:組件被停用(離開路由)時調用
使用了keep-alive
就不會調用beforeDestroy
(組件銷燬前鉤子)和destroyed
(組件銷燬),由於組件沒被銷燬,被緩存起來了。
這個鉤子能夠看做beforeDestroy
的替代,若是你緩存了組件,要在組件銷燬的的時候作一些事情,你能夠放在這個鉤子裏。
若是你離開了路由,會依次觸發:
組件內的離開當前路由鉤子beforeRouteLeave => 路由前置守衛 beforeEach =>
全局後置鉤子afterEach => deactivated 離開緩存組件 => activated 進入緩存組件(若是你進入的也是緩存路由)
// 若是離開的組件沒有緩存的話 beforeDestroy會替換deactivated
// 若是進入的路由也沒有緩存的話 全局後置鉤子afterEach=>銷燬 destroyed=> beforeCreate等
複製代碼
那麼,若是我只是想緩存其中幾個路由/組件,那該怎麼作?
想實現相似的操做,你能夠:
配置一下路由元信息
建立兩個keep-alive
標籤
使用v-if
經過路由元信息判斷緩存哪些路由。
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!--這裏是會被緩存的路由-->
</router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive">
<!--由於用的是v-if 因此下面還要建立一個未緩存的路由視圖出口-->
</router-view>
//router配置
new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
meta: {
keepAlive: true // 須要被緩存
}
},
{
path: '/:id',
name: 'edit',
component: Edit,
meta: {
keepAlive: false // 不須要被緩存
}
}
]
});
複製代碼
使用路由元信息的方式,要多建立一個router-view
標籤,而且每一個路由都要配置一個元信息,是能夠實現咱們想要的效果,可是過於繁瑣了點。
幸運的是在Vue2.1.0以後,Vue新增了兩個屬性配合keep-alive
來有條件地緩存 路由/組件。
新增屬性:
include
:匹配的 路由/組件 會被緩存exclude
:匹配的 路由/組件 不會被緩存include
和exclude
支持三種方式來有條件的緩存路由:採用逗號分隔的字符串形式,正則形式,數組形式。
正則和數組形式,必須採用v-bind
形式來使用。
緩存組件的使用方式:
<!-- 逗號分隔字符串 -->
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正則表達式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 數組 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
複製代碼
但更多場景中,咱們會使用keep-alive
來緩存路由:
<keep-alive include='a'>
<router-view></router-view>
</keep-alive>
複製代碼
匹配規則:
name
選項不可用。components
選項的鍵值)好比路由組件沒有name
選項,而且沒有註冊的組件名。
好比用在路由上,只能匹配路由組件的name
選項,不能匹配路由組件裏面的嵌套組件的name
選項。
<keep-alive>
不會在函數式組件中正常工做,由於它們沒有緩存實例。exclude
的優先級大於include
也就是說:當include
和exclude
同時存在時,exclude
生效,include
不生效。
<keep-alive include="a,b" exclude="a">
<!--只有a不被緩存-->
<router-view></router-view>
</keep-alive>
複製代碼
當組件被exclude
匹配,該組件將不會被緩存,不會調用activated
和 deactivated
。
關於組件的生命週期,是時候放出這張圖片了:
這張圖片已經講得很清楚了,不少人這部分也很清楚了,大部分生命週期並不會用到,這裏提一下幾點:
ajax請求最好放在created
裏面,由於此時已經能夠訪問this
了,請求到數據就能夠直接放在data
裏面。
這裏也碰到過幾回,面試官問:ajax請求應該放在哪一個生命週期。
關於dom的操做要放在mounted
裏面,在mounted
前面訪問dom會是undefined
。
每次進入/離開組件都要作一些事情,用什麼鉤子:
不緩存:
進入的時候能夠用created
和mounted
鉤子,離開的時候用beforeDestory
和destroyed
鉤子,beforeDestory
能夠訪問this
,destroyed
不能夠訪問this
。
緩存了組件:
緩存了組件以後,再次進入組件不會觸發beforeCreate
、created
、beforeMount
、 mounted
,若是你想每次進入組件都作一些事情的話,你能夠放在activated
進入緩存組件的鉤子中。
同理:離開緩存組件的時候,beforeDestroy
和destroyed
並不會觸發,可使用deactivated
離開緩存組件的鉤子來代替。
將路由導航、keep-alive
、和組件生命週期鉤子結合起來的,觸發順序,假設是從a組件離開,第一次進入b組件:
beforeRouteLeave
:路由組件的組件離開路由前鉤子,可取消路由離開。beforeEach
: 路由全局前置守衛,可用於登陸驗證、全局路由loading等。beforeEnter
: 路由獨享守衛beforeRouteEnter
: 路由組件的組件進入路由前鉤子。beforeResolve
:路由全局解析守衛afterEach
:路由全局後置鉤子beforeCreate
:組件生命週期,不能訪問this
。created
:組件生命週期,能夠訪問this
,不能訪問dom。beforeMount
:組件生命週期deactivated
: 離開緩存組件a,或者觸發a的beforeDestroy
和destroyed
組件銷燬鉤子。mounted
:訪問/操做dom。activated
:進入緩存組件,進入a的嵌套子組件(若是有的話)。Vue提供了不少鉤子,但不少鉤子咱們幾乎不會用到,只有清楚這些鉤子函數的觸發順序以及背後的一些限制等,這樣咱們纔可以正確的使用這些鉤子,但願看了本文的同窗,能對這些鉤子有更加清晰的認識,使用起來更加駕輕就熟。
前端進階積累、公衆號、GitHub、wx:OBkoro一、郵箱:obkoro1@foxmail.com
以上2018.7.21
參考資料:
Vue文檔