您好,登錄后才能下訂單哦!
說到Vue的鉤子函數(shù),可能很多人只停留在一些很簡(jiǎn)單常用的鉤子(created
,mounted
),而且對(duì)于里面的區(qū)別,什么時(shí)候該用什么鉤子,并沒有仔細(xì)的去研究過,且Vue的生命周期在面試中也算是比較高頻的考點(diǎn),那么該如何回答這類問題,讓人有眼前一亮的感覺呢...
有的時(shí)候,我們需要通過路由來進(jìn)行一些操作,比如最常見的登錄權(quán)限驗(yàn)證,當(dāng)用戶滿足條件時(shí),才讓其進(jìn)入導(dǎo)航,否則就取消跳轉(zhuǎn),并跳到登錄頁(yè)面讓其登錄。
為此我們有很多種方法可以植入路由的導(dǎo)航過程:全局的, 單個(gè)路由獨(dú)享的, 或者組件級(jí)的,推薦優(yōu)先閱讀路由文檔
vue-router全局有三個(gè)守衛(wèi):
router.beforeEach 全局前置守衛(wèi) 進(jìn)入路由之前
router.beforeResolve 全局解析守衛(wèi)(2.5.0+) 在beforeRouteEnter調(diào)用之后調(diào)用
router.afterEach 全局后置鉤子 進(jìn)入路由之后
使用方法:
// 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 全局后置鉤子'); });復(fù)制代碼
to和from是將要進(jìn)入和將要離開的路由對(duì)象,路由對(duì)象指的是平時(shí)通過this.$route獲取到的路由對(duì)象。
next:Function 這個(gè)參數(shù)是個(gè)函數(shù),且必須調(diào)用,否則不能進(jìn)入路由(頁(yè)面空白)。
next() 進(jìn)入該路由。
next(false): 取消進(jìn)入路由,url地址重置為from路由地址(也就是將要離開的路由地址)。
next 跳轉(zhuǎn)新路由,當(dāng)前的導(dǎo)航被中斷,重新開始一個(gè)新的導(dǎo)航。
我們可以這樣跳轉(zhuǎn):next('path地址')或者next({path:''})或者next({name:''}) 且允許設(shè)置諸如 replace: true、name: 'home' 之類的選項(xiàng) 以及你用在router-link或router.push的對(duì)象選項(xiàng)。復(fù)制代碼
如果你不想全局配置守衛(wèi)的話,你可以為某些路由單獨(dú)配置守衛(wèi):
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // 參數(shù)用法什么的都一樣,調(diào)用順序在全局前置守衛(wèi)后面,所以不會(huì)被全局守衛(wèi)覆蓋 // ... } } ] })復(fù)制代碼
beforeRouteEnter 進(jìn)入路由前
beforeRouteUpdate (2.2) 路由復(fù)用同一個(gè)組件時(shí)
beforeRouteLeave 離開當(dāng)前路由時(shí)
文檔中的介紹:
beforeRouteEnter (to, from, next) { // 在路由獨(dú)享守衛(wèi)后調(diào)用 不!能!獲取組件實(shí)例 `this`,組件實(shí)例還沒被創(chuàng)建 }, beforeRouteUpdate (to, from, next) { // 在當(dāng)前路由改變,但是該組件被復(fù)用時(shí)調(diào)用 可以訪問組件實(shí)例 `this` // 舉例來說,對(duì)于一個(gè)帶有動(dòng)態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時(shí)候, // 由于會(huì)渲染同樣的 Foo 組件,因此組件實(shí)例會(huì)被復(fù)用。而這個(gè)鉤子就會(huì)在這個(gè)情況下被調(diào)用。 }, beforeRouteLeave (to, from, next) { // 導(dǎo)航離開該組件的對(duì)應(yīng)路由時(shí)調(diào)用,可以訪問組件實(shí)例 `this` }復(fù)制代碼
beforeRouteEnter訪問this
因?yàn)殂^子在組件實(shí)例還沒被創(chuàng)建的時(shí)候調(diào)用,所以不能獲取組件實(shí)例 this
,可以通過傳一個(gè)回調(diào)給next
來訪問組件實(shí)例
。
但是回調(diào)的執(zhí)行時(shí)機(jī)在mounted后面,所以在我看來這里對(duì)this的訪問意義不太大,可以放在created
或者mounted
里面。
beforeRouteEnter (to, from, next) { console.log('在路由獨(dú)享守衛(wèi)后調(diào)用'); next(vm => { // 通過 `vm` 訪問組件實(shí)例`this` 執(zhí)行回調(diào)的時(shí)機(jī)在mounted后面, }) }復(fù)制代碼
beforeRouteLeave:
導(dǎo)航離開該組件的對(duì)應(yīng)路由時(shí)調(diào)用,我們用它來禁止用戶離開,比如還未保存草稿,或者在用戶離開前,將setInterval
銷毀,防止離開之后,定時(shí)器還在調(diào)用。
beforeRouteLeave (to, from , next) { if (文章保存) { next(); // 允許離開或者可以跳到別的路由 上面講過了 } else { next(false); // 取消離開 } }復(fù)制代碼
如果我們?cè)谌质匦l(wèi)/路由獨(dú)享守衛(wèi)/組件路由守衛(wèi)的鉤子函數(shù)中有錯(cuò)誤,可以這樣捕獲:
router.onError(callback => { // 2.4.0新增 并不常用,了解一下就可以了 console.log(callback, 'callback'); });復(fù)制代碼
在路由文檔中還有更多的實(shí)例方法:動(dòng)態(tài)添加路由等,有興趣可以了解一下。
我了解到的,很多人會(huì)碰到這個(gè)問題,來看一下這段偽代碼:
router.beforeEach((to, from, next) => { if(登錄){ next() }else{ next({ name: 'login' }); } });復(fù)制代碼
看邏輯貌似是對(duì)的,但是當(dāng)我們跳轉(zhuǎn)到login
之后,因?yàn)榇藭r(shí)還是未登錄狀態(tài),所以會(huì)一直跳轉(zhuǎn)到login
然后死循環(huán),頁(yè)面一直是空白的,所以:我們需要把判斷條件稍微改一下。
if(登錄 || to.name === 'login'){ next() } // 登錄,或者將要前往login頁(yè)面的時(shí)候,就允許進(jìn)入路由復(fù)制代碼
文檔中提到因?yàn)閞outer.afterEach不接受next
函數(shù)所以也不會(huì)改變導(dǎo)航本身,意思就是只能當(dāng)成一個(gè)鉤子來使用,但是我自己在試的時(shí)候發(fā)現(xiàn),我們可以通過這種形式來實(shí)現(xiàn)跳轉(zhuǎn):
// main.js 入口文件 import router from './router'; // 引入路由 router.afterEach((to, from) => { if (未登錄 && to.name !== 'login') { router.push({ name: 'login' }); // 跳轉(zhuǎn)login } });復(fù)制代碼
額,通過router.beforeEach 也完全可以實(shí)現(xiàn)且更好,我就騷一下。
觸發(fā)進(jìn)入其他路由。
調(diào)用要離開路由的組件守衛(wèi)beforeRouteLeave
調(diào)用局前置守衛(wèi):beforeEach
在重用的組件里調(diào)用 beforeRouteUpdate
調(diào)用路由獨(dú)享守衛(wèi) beforeEnter
。
解析異步路由組件。
在將要進(jìn)入的路由組件中調(diào)用beforeRouteEnter
調(diào)用全局解析守衛(wèi) beforeResolve
導(dǎo)航被確認(rèn)。
調(diào)用全局后置鉤子的 afterEach
鉤子。
觸發(fā)DOM更新(mounted
)。
執(zhí)行beforeRouteEnter
守衛(wèi)中傳給 next 的回調(diào)函數(shù)
在開發(fā)Vue項(xiàng)目的時(shí)候,大部分組件是沒必要多次渲染的,所以Vue提供了一個(gè)內(nèi)置組件keep-alive
來緩存組件內(nèi)部狀態(tài),避免重新渲染,文檔在這里。
文檔:和
<transition>
相似,<keep-alive>
是一個(gè)抽象組件:它自身不會(huì)渲染一個(gè) DOM 元素,也不會(huì)出現(xiàn)在父組件鏈中。
緩存動(dòng)態(tài)組件:
<keep-alive>
包裹動(dòng)態(tài)組件時(shí),會(huì)緩存不活動(dòng)的組件實(shí)例,而不是銷毀它們,此種方式并無太大的實(shí)用意義。
<!-- 基本 --> <keep-alive> <component :is="view"></component> </keep-alive> <!-- 多個(gè)條件判斷的子組件 --> <keep-alive> <comp-a v-if="a > 1"></comp-a> <comp-b v-else></comp-b> </keep-alive>復(fù)制代碼
緩存路由組件:
使用keep-alive
可以將所有路徑匹配到的路由組件都緩存起來,包括路由組件里面的組件,keep-alive
大多數(shù)使用場(chǎng)景就是這種。
<keep-alive> <router-view></router-view> </keep-alive>復(fù)制代碼
這篇既然是Vue鉤子函數(shù)的專場(chǎng),那肯定要扣題呀~
在被keep-alive
包含的組件/路由中,會(huì)多出兩個(gè)生命周期的鉤子:activated
與 deactivated
。
文檔:在 2.2.0 及其更高版本中,activated 和 deactivated 將會(huì)在 樹內(nèi)的所有嵌套組件中觸發(fā)。
activated在組件第一次渲染時(shí)會(huì)被調(diào)用,之后在每次緩存組件被激活時(shí)調(diào)用。
activated調(diào)用時(shí)機(jī):
第一次進(jìn)入緩存路由/組件,在mounted
后面,beforeRouteEnter
守衛(wèi)傳給 next 的回調(diào)函數(shù)之前調(diào)用:
beforeMount=> 如果你是從別的路由/組件進(jìn)來(組件銷毀destroyed/或離開緩存deactivated)=> mounted=> activated 進(jìn)入緩存組件 => 執(zhí)行 beforeRouteEnter回調(diào)復(fù)制代碼
因?yàn)榻M件被緩存了,再次進(jìn)入緩存路由/組件時(shí),不會(huì)觸發(fā)這些鉤子:
// beforeCreate created beforeMount mounted 都不會(huì)觸發(fā)。
所以之后的調(diào)用時(shí)機(jī)是:
組件銷毀destroyed/或離開緩存deactivated => activated 進(jìn)入當(dāng)前緩存組件 => 執(zhí)行 beforeRouteEnter回調(diào) // 組件緩存或銷毀,嵌套組件的銷毀和緩存也在這里觸發(fā)
deactivated:組件被停用(離開路由)時(shí)調(diào)用
使用了keep-alive
就不會(huì)調(diào)用beforeDestroy
(組件銷毀前鉤子)和destroyed
(組件銷毀),因?yàn)榻M件沒被銷毀,被緩存起來了。
這個(gè)鉤子可以看作beforeDestroy
的替代,如果你緩存了組件,要在組件銷毀的的時(shí)候做一些事情,你可以放在這個(gè)鉤子里。
如果你離開了路由,會(huì)依次觸發(fā):
組件內(nèi)的離開當(dāng)前路由鉤子beforeRouteLeave => 路由前置守衛(wèi) beforeEach => 全局后置鉤子afterEach => deactivated 離開緩存組件 => activated 進(jìn)入緩存組件(如果你進(jìn)入的也是緩存路由) // 如果離開的組件沒有緩存的話 beforeDestroy會(huì)替換deactivated // 如果進(jìn)入的路由也沒有緩存的話 全局后置鉤子afterEach=>銷毀 destroyed=> beforeCreate等復(fù)制代碼
那么,如果我只是想緩存其中幾個(gè)路由/組件,那該怎么做?
想實(shí)現(xiàn)類似的操作,你可以:
配置一下路由元信息
創(chuàng)建兩個(gè)keep-alive
標(biāo)簽
使用v-if
通過路由元信息判斷緩存哪些路由。
<keep-alive> <router-view v-if="$route.meta.keepAlive"> <!--這里是會(huì)被緩存的路由--> </router-view> </keep-alive> <router-view v-if="!$route.meta.keepAlive"> <!--因?yàn)橛玫氖莢-if 所以下面還要?jiǎng)?chuàng)建一個(gè)未緩存的路由視圖出口--> </router-view> //router配置 new Router({ routes: [ { path: '/', name: 'home', component: Home, meta: { keepAlive: true // 需要被緩存 } }, { path: '/:id', name: 'edit', component: Edit, meta: { keepAlive: false // 不需要被緩存 } } ] });復(fù)制代碼
使用路由元信息的方式,要多創(chuàng)建一個(gè)router-view
標(biāo)簽,并且每個(gè)路由都要配置一個(gè)元信息,是可以實(shí)現(xiàn)我們想要的效果,但是過于繁瑣了點(diǎn)。
幸運(yùn)的是在Vue2.1.0之后,Vue新增了兩個(gè)屬性配合keep-alive
來有條件地緩存 路由/組件。
新增屬性:
include
:匹配的 路由/組件 會(huì)被緩存
exclude
:匹配的 路由/組件 不會(huì)被緩存
include
和exclude
支持三種方式來有條件的緩存路由:采用逗號(hào)分隔的字符串形式,正則形式,數(shù)組形式。
正則和數(shù)組形式,必須采用v-bind
形式來使用。
緩存組件的使用方式:
<!-- 逗號(hào)分隔字符串 --> <keep-alive include="a,b"> <component :is="view"></component> </keep-alive> <!-- 正則表達(dá)式 (使用 `v-bind`) --> <keep-alive :include="/a|b/"> <component :is="view"></component> </keep-alive> <!-- 數(shù)組 (使用 `v-bind`) --> <keep-alive :include="['a', 'b']"> <component :is="view"></component> </keep-alive>復(fù)制代碼
但更多場(chǎng)景中,我們會(huì)使用keep-alive
來緩存路由:
<keep-alive include='a'> <router-view></router-view> </keep-alive>復(fù)制代碼
匹配規(guī)則:
首先匹配組件的name選項(xiàng),如果name
選項(xiàng)不可用。
則匹配它的局部注冊(cè)名稱。 (父組件 components
選項(xiàng)的鍵值)
匿名組件,不可匹配。
比如路由組件沒有name
選項(xiàng),并且沒有注冊(cè)的組件名。
只能匹配當(dāng)前被包裹的組件,不能匹配更下面嵌套的子組件。
比如用在路由上,只能匹配路由組件的name
選項(xiàng),不能匹配路由組件里面的嵌套組件的name
選項(xiàng)。
文檔:<keep-alive>
不會(huì)在函數(shù)式組件中正常工作,因?yàn)樗鼈儧]有緩存實(shí)例。
exclude
的優(yōu)先級(jí)大于include
也就是說:當(dāng)include
和exclude
同時(shí)存在時(shí),exclude
生效,include
不生效。
<keep-alive include="a,b" exclude="a"> <!--只有a不被緩存--> <router-view></router-view> </keep-alive>復(fù)制代碼
當(dāng)組件被exclude
匹配,該組件將不會(huì)被緩存,不會(huì)調(diào)用activated
和 deactivated
。
關(guān)于組件的生命周期,是時(shí)候放出這張圖片了:
這張圖片已經(jīng)講得很清楚了,很多人這部分也很清楚了,大部分生命周期并不會(huì)用到,這里提一下幾點(diǎn):
ajax請(qǐng)求最好放在created
里面,因?yàn)榇藭r(shí)已經(jīng)可以訪問this
了,請(qǐng)求到數(shù)據(jù)就可以直接放在data
里面。
這里也碰到過幾次,面試官問:ajax請(qǐng)求應(yīng)該放在哪個(gè)生命周期。
關(guān)于dom的操作要放在mounted
里面,在mounted
前面訪問dom會(huì)是undefined
。
每次進(jìn)入/離開組件都要做一些事情,用什么鉤子:
不緩存:
進(jìn)入的時(shí)候可以用created
和mounted
鉤子,離開的時(shí)候用beforeDestory
和destroyed
鉤子,beforeDestory
可以訪問this
,destroyed
不可以訪問this
。
緩存了組件:
緩存了組件之后,再次進(jìn)入組件不會(huì)觸發(fā)beforeCreate
、created
、beforeMount
、 mounted
,如果你想每次進(jìn)入組件都做一些事情的話,你可以放在activated
進(jìn)入緩存組件的鉤子中。
同理:離開緩存組件的時(shí)候,beforeDestroy
和destroyed
并不會(huì)觸發(fā),可以使用deactivated
離開緩存組件的鉤子來代替。
將路由導(dǎo)航、keep-alive
、和組件生命周期鉤子結(jié)合起來的,觸發(fā)順序,假設(shè)是從a組件離開,第一次進(jìn)入b組件:
beforeRouteLeave
:路由組件的組件離開路由前鉤子,可取消路由離開。
beforeEach
: 路由全局前置守衛(wèi),可用于登錄驗(yàn)證、全局路由loading等。
beforeEnter
: 路由獨(dú)享守衛(wèi)
beforeRouteEnter
: 路由組件的組件進(jìn)入路由前鉤子。
beforeResolve
:路由全局解析守衛(wèi)
afterEach
:路由全局后置鉤子
beforeCreate
:組件生命周期,不能訪問this
。
created
:組件生命周期,可以訪問this
,不能訪問dom。
beforeMount
:組件生命周期
deactivated
: 離開緩存組件a,或者觸發(fā)a的beforeDestroy
和destroyed
組件銷毀鉤子。
mounted
:訪問/操作dom。
activated
:進(jìn)入緩存組件,進(jìn)入a的嵌套子組件(如果有的話)。
執(zhí)行beforeRouteEnter回調(diào)函數(shù)next。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。