組件相關(guān)鉤子函數(shù): beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destoryed
還有兩個(gè)特殊的(使用keep-alive):activated、deactivated(不詳述)
v2.5.0+新增: errorCaptured (暫時(shí)還不知道咋用)
路由守衛(wèi):
全局&路由獨(dú)享:beforeEach、beforeResolve(v2.5.0+新增)、afterEach ;beforeEnter(路由獨(dú)享,類似beforeEach)
組件內(nèi):beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave
組件生命周期鉤子函數(shù)
beforeCreate
實(shí)例初始化之后
this指向創(chuàng)建的實(shí)例
數(shù)據(jù)觀測(cè)(data observer)和event/watcher配置尚未完成
不能訪問到methods、data、computed、watch上的方法和數(shù)據(jù)
created
實(shí)例創(chuàng)建完成,并已經(jīng)完成以下配置:數(shù)據(jù)觀測(cè)(data observer),屬性和方法的運(yùn)算, watch/event 事件回調(diào)
此時(shí)可以調(diào)用methods中定義的方法,修改data的數(shù)據(jù),并且可觸發(fā)響應(yīng)式變化、computed值重新計(jì)算,watch到變更等
還未掛載到DOM,不能訪問到$el屬性,$ref屬性內(nèi)容為空數(shù)組
new Vue({
data () {
return {
a : 1
}
} ,
created (){
console.log( this.a ) // 1
}
})
這個(gè)生命周期階段比較常用,比如ajax請(qǐng)求獲取初始化數(shù)據(jù)對(duì)實(shí)例進(jìn)行初始化預(yù)處理等操作;但要注意在結(jié)合vue-router使用時(shí),進(jìn)入created生命周期階段后是無法對(duì)掛載實(shí)例進(jìn)行攔截的,此時(shí)實(shí)例已經(jīng)創(chuàng)建完成;故如果需要某些數(shù)據(jù)獲取完成情況才允許進(jìn)入頁(yè)面的場(chǎng)景,建議在路由鉤子beforeRouteEnter中實(shí)現(xiàn)
beforeMount
在掛載開始之前被調(diào)用
注意:從vue生命周期圖可以看出
當(dāng)new Vue({...})的配置中沒有el屬性時(shí),生命周期暫停,等待vm.$mount(el)調(diào)用時(shí)才繼續(xù)
beforeMount之前,會(huì)找到對(duì)應(yīng)的template,并編譯成render函數(shù)
(這個(gè)步驟如果使用.vue文件和運(yùn)行時(shí)版本將會(huì)在構(gòu)建時(shí)提前完成)
template查找的優(yōu)先級(jí)順序:
** template參數(shù) > el 外部HTML**
如果指定了render函數(shù),則直接采用render函數(shù),即忽略template參數(shù)和el外部HTML
寫個(gè)栗子測(cè)試:
<div id="app">template outside</div>
...
import App from './App.vue'; // App是任一Vue組件對(duì)象
new Vue({
el: '#app',
// template: '<p>template inside</p>', // part inside
// render: h => h(App) // part render
})
需要Vue完整版本支持,注釋part inside和part render依次打開即可觀察到三次不同的結(jié)果
Vue的編譯過程暫略
mounted
el被新創(chuàng)建的$el替換 ---- 怎么理解?
這個(gè)可以理解為掛載前為實(shí)例尋找了一個(gè)暫時(shí)容身之處el,編譯完成($el創(chuàng)建完成)后替換這個(gè)容身之處完成實(shí)例的掛載
如:之前那個(gè)栗子中,將part render打開后觀察生成的DOM結(jié)構(gòu),<div id="app">template outside</div>
這個(gè)節(jié)點(diǎn)即原el已經(jīng)被替換掉
實(shí)例掛載到DOM上,此時(shí)可以通過DOM API獲取到DOM節(jié)點(diǎn),$ref屬性可以訪問
雖然經(jīng)常觀察到先進(jìn)入子組件mounted,但根據(jù)Vue官方文檔:
注意 `mounted` 不會(huì)承諾所有的子組件也都一起被掛載。如果你希望等到整個(gè)視圖都渲染
完畢,可以用 [vm.$nextTick](https://cn.vuejs.org/v2/api/#vm-nextTick)
在這個(gè)階段改變data上的值,相關(guān)的computed屬性可以立刻更新;但需要進(jìn)入到下一次DOM更新,才能看到DOM上數(shù)據(jù)更新
栗子:
new Vue({
el: '#app',
template: '<p id="testa">{{a}}</p>',
router,
data ()
{
return {
a : 0
}
},
mounted() {
this.a ++;
console.log(this.b); // 2
console.log(document.getElementById('testa').innerHTML) // 0
},
computed :{
b (){
return this.a+1;
}
}
})
beforeRouteEnter的next的勾子比mounted觸發(fā)還要靠后 - 這個(gè)待會(huì)說到路由相關(guān)鉤子時(shí)再展開
beforeUpdate
這里的更新對(duì)象是模板,即需要虛擬 DOM 重新渲染和打補(bǔ)丁,beforeUpdate發(fā)生在以上兩個(gè)流程之前,此時(shí)新的虛擬DOM已經(jīng)生成
如果發(fā)生變更的數(shù)據(jù)在模板中并沒有使用(包括直接和間接,間接:比如某個(gè)依賴該數(shù)據(jù)的計(jì)算屬性在模板中使用了),則不會(huì)觸發(fā)更新流程!!!
如:
new Vue({
el: '#app',
template: '<p id="testa">{{a}}</p>',
router,
data ()
{
return {
a : 0,
b : 1
}
},
mounted (){
this.b ++; // b并沒有在模板中使用
},
beforeUpdate (){
debugger; // 不會(huì)進(jìn)入這個(gè)斷點(diǎn)
}
})
在一些參考文章中看到:
在這個(gè)鉤子函數(shù)中,可以對(duì)狀態(tài)進(jìn)行進(jìn)一步更改,不會(huì)再次觸發(fā)重渲染流程
--- 這個(gè)說法有問題
如果對(duì)狀態(tài)進(jìn)行變更會(huì)導(dǎo)致重新進(jìn)入beforeUpdate(這里變更的狀態(tài)同樣要在模板中使用,如果變更沒有在模板中使用的data,才不會(huì)再次觸發(fā)重渲染流程)
而且若變更操作不是基礎(chǔ)類型的簡(jiǎn)單賦值,還會(huì)引起死循環(huán)(不斷重新進(jìn)入beforeUpdate)!
看看這個(gè)栗子,依次把注釋打開測(cè)試
new Vue({
el: '#app',
template: '<p id="testa">{{a}}</p>',
router,
data ()
{
return {
a : 0,
c: 0
}
},
beforeUpdate() {
console.log(document.getElementById('testa').innerHTML)
// this.c = 1; // this.c沒有在模板中使用,變更不會(huì)引起重渲染流程
// this.a = 3; // 會(huì)再次進(jìn)入一次重渲染流程,第二次進(jìn)入時(shí)發(fā)現(xiàn)a仍是3,值沒有變更,不會(huì)再次重渲染
// this.a ++; // 會(huì)引起死循環(huán),每次進(jìn)入重渲染流程時(shí),a的值都會(huì)變更
},
updated() {
console.log(document.getElementById('testa').innerHTML)
}
})
應(yīng)該避免在這個(gè)鉤子函數(shù)中操作數(shù)據(jù)
updated
由于數(shù)據(jù)更改導(dǎo)致的虛擬 DOM 重新渲染和打補(bǔ)丁,在這之后會(huì)調(diào)用該鉤子。
當(dāng)這個(gè)鉤子被調(diào)用時(shí),組件 DOM 已經(jīng)更新,可以執(zhí)行依賴于 DOM 的操作
注意 `updated` 不會(huì)承諾所有的子組件也都一起被重繪。如果你希望等到整個(gè)視圖都重繪完畢,
可以用 [vm.$nextTick](https://cn.vuejs.org/v2/api/#vm-nextTick)
同樣,應(yīng)該避免在這個(gè)鉤子函數(shù)中操作數(shù)據(jù)
beforeDestroy
實(shí)例銷毀之前調(diào)用。在這一步,實(shí)例仍然完全可用,this仍能獲取到實(shí)例
一般在這一步中進(jìn)行:銷毀定時(shí)器、解綁全局事件、銷毀插件對(duì)象等操作
destroyed
Vue 實(shí)例銷毀后調(diào)用。調(diào)用后,Vue 實(shí)例指示的所有東西都會(huì)解綁定,所有的事件監(jiān)聽器會(huì)被移除,所有的子實(shí)例也會(huì)被銷毀
注意:vue2.0之后主動(dòng)調(diào)用$destroy()不會(huì)移除dom節(jié)點(diǎn),作者不推薦直接destroy這種做法,具體參考https://github.com/vuejs/vue/issues/5256,如果實(shí)在需要這樣用可以在這個(gè)生命周期鉤子中手動(dòng)移除dom節(jié)點(diǎn)
總結(jié)
路由守衛(wèi) —— 路由級(jí)別的(全局&路由獨(dú)享)
router.beforeEach
全局前置守衛(wèi)
當(dāng)一個(gè)導(dǎo)航觸發(fā)時(shí),全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用。守衛(wèi)是異步解析執(zhí)行,此時(shí)導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于 等待中
如何使用:
router.beforeEach((to, from, next) => {
console.log('全局前置守衛(wèi):beforeEach -- next需要調(diào)用')
next()
})
一般在這個(gè)守衛(wèi)方法中進(jìn)行全局?jǐn)r截,比如必須滿足某種條件(用戶登錄等)才能進(jìn)入路由的情況
參數(shù)to和from都是路由對(duì)象Route
next是個(gè)Function,有以下幾種用法(from api文檔)
- 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)的地址 —— 也就是說并不是單純的中斷,還會(huì)檢查URL的變更以保證不會(huì)錯(cuò)誤的進(jìn)入到next路由
- next('/') 或者 next({ path: '/' }): 跳轉(zhuǎn)到一個(gè)不同的地址。當(dāng)前的導(dǎo)航被中斷,然后進(jìn)行一個(gè)新的導(dǎo)航。可傳遞的參數(shù)與router.push中選項(xiàng)一致
- next(error): (v2.4.0+) 如果傳入
next
的參數(shù)是一個(gè)Error
實(shí)例,則導(dǎo)航會(huì)被終止且該錯(cuò)誤會(huì)被傳遞給router.onError()
注冊(cè)過的回調(diào)
router.beforeResolve (v 2.5.0+)
全局解析守衛(wèi)
和beforeEach類似,區(qū)別是在導(dǎo)航被確認(rèn)之前,同時(shí)在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后,解析守衛(wèi)就被調(diào)用
即在 beforeEach 和 組件內(nèi)beforeRouteEnter 之后
參數(shù)和beforeEach一致,也需要調(diào)用next對(duì)導(dǎo)航確認(rèn)
router.afterEach
全局后置鉤子
在所有路由跳轉(zhuǎn)結(jié)束的時(shí)候調(diào)用
這些鉤子不會(huì)接受 next 函數(shù)也不會(huì)改變導(dǎo)航本身
beforeEnter
可直接定義在路由配置上,和beforeEach方法參數(shù)、用法相同
路由守衛(wèi) —— 組件內(nèi)
beforeRouteEnter
在渲染該組件的對(duì)應(yīng)路由被確認(rèn)
前調(diào)用,用法和參數(shù)與beforeEach類似,next需要被主動(dòng)調(diào)用
注意:
- 此時(shí)組件實(shí)例還未被創(chuàng)建,不能訪問this
- 可以通過傳一個(gè)回調(diào)給 next來訪問組件實(shí)例。在導(dǎo)航被確認(rèn)的時(shí)候執(zhí)行回調(diào),并且把組件實(shí)例作為回調(diào)方法的參數(shù)
beforeRouteEnter (to, from, next) {
// 這里還無法訪問到組件實(shí)例,this === undefined
next( vm => {
// 通過 `vm` 訪問組件實(shí)例
})
}
- 可以在這個(gè)守衛(wèi)中請(qǐng)求服務(wù)端獲取數(shù)據(jù),當(dāng)成功獲取并能進(jìn)入路由時(shí),調(diào)用next并在回調(diào)中通過
vm
訪問組件實(shí)例進(jìn)行賦值等操作 - beforeRouteEnter觸發(fā)在導(dǎo)航確認(rèn)、組件實(shí)例創(chuàng)建之前:beforeCreate之前;而next中函數(shù)的調(diào)用在mounted之后:為了確保能對(duì)組件實(shí)例的完整訪問
beforeRouteUpdate (v 2.2+)
在當(dāng)前路由改變,并且該組件被復(fù)用時(shí)調(diào)用,可以通過this訪問實(shí)例, next需要被主動(dòng)調(diào)用,不能傳回調(diào)
- 對(duì)于一個(gè)帶有動(dòng)態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時(shí)候,組件實(shí)例會(huì)被復(fù)用,該守衛(wèi)會(huì)被調(diào)用
- 當(dāng)前路由query變更時(shí),該守衛(wèi)會(huì)被調(diào)用
- vue-router推薦的數(shù)據(jù)獲取方法二中,結(jié)合beforeRouteEnter使用,在路由參數(shù)變更時(shí)可以重新獲取數(shù)據(jù),獲取成功再調(diào)用next(),參考:https://router.vuejs.org/zh-cn/advanced/data-fetching.html
之前在手機(jī)瀏覽器中好像發(fā)現(xiàn)這個(gè)守衛(wèi)的bug?@TODO 待確認(rèn)
beforeRouteLeave
導(dǎo)航離開該組件的對(duì)應(yīng)路由時(shí)調(diào)用,可以訪問組件實(shí)例 this
,next需要被主動(dòng)調(diào)用,不能傳回調(diào)
總結(jié)
結(jié)合并擴(kuò)展Vue-router官方文檔的說明:
- 導(dǎo)航行為被觸發(fā),此時(shí)導(dǎo)航未被確認(rèn)。
- 在失活的組件里調(diào)用離開守衛(wèi) beforeRouteLeave。
- 調(diào)用全局的 beforeEach 守衛(wèi)。
- 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)。
- 在路由配置里調(diào)用 beforeEnter。
- 解析異步路由組件(如果有)。
- 在被激活的組件里調(diào)用 beforeRouteEnter。
- 調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+),標(biāo)示解析階段完成。
- 導(dǎo)航被確認(rèn)。
- 調(diào)用全局的 afterEach 鉤子。
- 非重用組件,開始組件實(shí)例的生命周期
- beforeCreate&created
- beforeMount&mounted
- 觸發(fā) DOM 更新。
- 用創(chuàng)建好的實(shí)例調(diào)用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調(diào)函數(shù)。
- 導(dǎo)航完成