深入理解Vue父子組件生命周期執(zhí)行順序及鉤子函數(shù)
先附一張官網(wǎng)上的vue實(shí)例的生命周期圖,每個(gè)Vue實(shí)例在被創(chuàng)建的時(shí)候都需要經(jīng)過(guò)一系列的初始化過(guò)程,例如需要設(shè)置數(shù)據(jù)監(jiān)聽,編譯模板,將實(shí)例掛載到DOM并在數(shù)據(jù)變化時(shí)更新DOM等。同時(shí)在這個(gè)過(guò)程中也會(huì)運(yùn)行一些叫做生命周期鉤子的函數(shù)(回調(diào)函數(shù)),這給了用戶在不同階段添加自己代碼的機(jī)會(huì)。
1、vue的生命周期圖
在vue實(shí)例的整個(gè)生命周期的各個(gè)階段,會(huì)提供不同的鉤子函數(shù)以供我們進(jìn)行不同的操作。先列出vue官網(wǎng)上對(duì)各個(gè)鉤子函數(shù)的詳細(xì)解析。
生命周期鉤子 |
詳細(xì) |
---|---|
beforeCreate | 在實(shí)例初始化之后,數(shù)據(jù)觀測(cè)(data observer) 和 event/watcher 事件配置之前被調(diào)用。 |
created | 實(shí)例已經(jīng)創(chuàng)建完成之后被調(diào)用。在這一步,實(shí)例已完成以下的配置:數(shù)據(jù)觀測(cè)(data observer),屬性和方法的運(yùn)算, watch/event 事件回調(diào)。然而,掛載階段還沒(méi)開始,$el 屬性目前不可見。 |
beforeMount | 在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用。 |
mounted | el 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用該鉤子。如果 root 實(shí)例掛載了一個(gè)文檔內(nèi)元素,當(dāng) mounted 被調(diào)用時(shí) vm.$el 也在文檔內(nèi)。 |
beforeUpdate | 數(shù)據(jù)更新時(shí)調(diào)用,發(fā)生在虛擬 DOM 重新渲染和打補(bǔ)丁之前。你可以在這個(gè)鉤子中進(jìn)一步地更改狀態(tài),這不會(huì)觸發(fā)附加的重渲染過(guò)程。 |
updated | 由于數(shù)據(jù)更改導(dǎo)致的虛擬 DOM 重新渲染和打補(bǔ)丁,在這之后會(huì)調(diào)用該鉤子。當(dāng)這個(gè)鉤子被調(diào)用時(shí),組件 DOM 已經(jīng)更新,所以你現(xiàn)在可以執(zhí)行依賴于 DOM 的操作。 |
activated | keep-alive 組件激活時(shí)調(diào)用。 |
deactivated | keep-alive 組件停用時(shí)調(diào)用。 |
beforeDestroy | 實(shí)例銷毀之前調(diào)用。在這一步,實(shí)例仍然完全可用。 |
destroyed | Vue 實(shí)例銷毀后調(diào)用。調(diào)用后,Vue 實(shí)例指示的所有東西都會(huì)解綁定,所有的事件監(jiān)聽器會(huì)被移除,所有的子實(shí)例也 |
實(shí)例銷毀后調(diào)用。調(diào)用后,Vue 實(shí)例指示的所有東西都會(huì)解綁定,所有的事件監(jiān)聽器會(huì)被移除,所有的子實(shí)例也會(huì)被銷毀。
2、實(shí)際操作
下面我們?cè)趯?shí)際的代碼執(zhí)行過(guò)程中理解父子組件生命周期創(chuàng)建過(guò)程以及鉤子函數(shù)執(zhí)行的實(shí)時(shí)狀態(tài)變化。
測(cè)試基于下面的代碼,引入vue.js文件后即可執(zhí)行。(打開頁(yè)面后,再按一次刷新會(huì)自動(dòng)進(jìn)入debugger狀態(tài))
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> </style> </head> <body> <div id="app"> <p>{{message}}</p> <keep-alive> <my-components :msg="msg1" v-if="show"></my-components> </keep-alive> </div> </body> <script src="../../node_modules/vue/dist/vue.js"></script> <script> var child = { template: '<div>from child: {{childMsg}}</div>', props: ['msg'], data: function() { return { childMsg: 'child' } }, beforeCreate: function () { debugger; }, created: function () { debugger; }, beforeMount: function () { debugger; }, mounted: function () { debugger; }, deactivated: function(){ alert("keepAlive停用"); }, activated: function () { console.log('component activated'); }, beforeDestroy: function () { console.group('beforeDestroy 銷毀前狀態(tài)===============》'); var state = { 'el': this.$el, 'data': this.$data, 'message': this.message } console.log(this.$el); console.log(state); }, destroyed: function () { console.group('destroyed 銷毀完成狀態(tài)===============》'); var state = { 'el': this.$el, 'data': this.$data, 'message': this.message } console.log(this.$el); console.log(state); }, }; var vm = new Vue({ el: '#app', data: { message: 'father', msg1: "hello", show: true }, beforeCreate: function () { debugger; }, created: function () { debugger; }, beforeMount: function () { debugger; }, mounted: function () { debugger; }, beforeUpdate: function () { alert("頁(yè)面視圖更新前"); }, updated: function () { alert("頁(yè)面視圖更新后"); }, beforeDestroy: function () { console.group('beforeDestroy 銷毀前狀態(tài)===============》'); var state = { 'el': this.$el, 'data': this.$data, 'message': this.message } console.log(this.$el); console.log(state); }, destroyed: function () { console.group('destroyed 銷毀完成狀態(tài)===============》'); var state = { 'el': this.$el, 'data': this.$data, 'message': this.message } console.log(this.$el); console.log(state); }, components: { 'my-components': child } }); </script> </html>
3.1、生命周期調(diào)試
首先我們創(chuàng)建了一個(gè)Vue實(shí)例vm,將其掛載到頁(yè)面中id為“app”的元素上。
3.1.1、根組件的beforeCreate階段
可以看出,在調(diào)用beforeCreate()函數(shù)時(shí),只進(jìn)行了一些必要的初始化操作(例如一些全局的配置和根實(shí)例的一些屬性初始化),此時(shí)data屬性為undefined,沒(méi)有可供操作的數(shù)據(jù)。
3.1.2、根組件的Created階段
調(diào)用Created()函數(shù),在這一步,實(shí)例已完成以下的配置:數(shù)據(jù)代理和動(dòng)態(tài)數(shù)據(jù)綁定(data observer),屬性和方法的運(yùn)算, watch/event 事件回調(diào)。然而,掛載階段還沒(méi)開始,$el 屬性目前不可見。
3.1.3、根組件的beforeMount階段
在調(diào)用boforeMount()函數(shù)前首先會(huì)判斷對(duì)象是否有el選項(xiàng)。如果有的話就繼續(xù)向下編譯,如果沒(méi)有el選項(xiàng),則停止編譯,也就意味著停止了生命周期,直到在該vue實(shí)例上調(diào)用vm.$mount(el)
在這個(gè)例子中,我們有el元素,因此會(huì)調(diào)用boforeMount()函數(shù),此時(shí)已經(jīng)開始執(zhí)行模板解析函數(shù),但還沒(méi)有將$el元素掛載頁(yè)面,頁(yè)面視圖因此也未更新。在標(biāo)紅處,還是 {{message}},這里就是應(yīng)用的Virtual DOM
(虛擬Dom)技術(shù),先把坑占住了。到后面mounted
掛載的時(shí)候再把值渲染進(jìn)去。
3.1.4、子組件的beforeCreate、Created、beforeMount、Mounted階段
在父組件執(zhí)行beforeMount階段后,進(jìn)入子組件的beforeCreate、Created、beforeMount階段,這些階段和父組件類似,按下不表。beforeMount階段后,執(zhí)行的是Mounted階段,該階段時(shí)子組件已經(jīng)掛載到父組件上,并且父組件隨之掛載到頁(yè)面中。
由下圖可以知道,在beforeMount階段之后、Mounted階段之前,數(shù)據(jù)已經(jīng)被加載到視圖上了,即$el元素被掛載到頁(yè)面時(shí)觸發(fā)了視圖的更新。
3.1.5、子組件的activated階段
我們發(fā)現(xiàn)在子父組件全部掛載到頁(yè)面之后被觸發(fā)。這是因?yàn)樽咏M件my-components被<keep-alive> 包裹,隨$el的掛載被觸發(fā)。如果子組件沒(méi)有被<keep-alive>包裹,那么該階段將不會(huì)被觸發(fā)。
3.1.6、父組件的mounted階段
mounted執(zhí)行時(shí):此時(shí)el已經(jīng)渲染完成并掛載到實(shí)例上。
至此,從Vue實(shí)例的初始化到將新的模板掛載到頁(yè)面上的階段已經(jīng)完成,退出debugger。下面我們來(lái)看一下deactivated、beforeUpdate、updated、beforeDestroy、destroyed鉤子函數(shù)。
3.2、deactivated、beforeUpdate、updated階段
由生命周期函數(shù)可知:當(dāng)數(shù)據(jù)變化后、虛擬DOM渲染重新渲染頁(yè)面前會(huì)觸發(fā)beforeUpdate()函數(shù),此時(shí)視圖還未改變。當(dāng)虛擬DOM渲染頁(yè)面視圖更新后會(huì)觸發(fā)updated()函數(shù)。
我們不妨改變vm.show = false,當(dāng)修改這個(gè)屬性時(shí),不僅會(huì)觸發(fā)beforeUpdate、updated函數(shù),還會(huì)觸發(fā)deactivated函數(shù)(因?yàn)閗eep-alive 組件停用時(shí)調(diào)用)。我們不妨想一下deactivated函數(shù)會(huì)在beforeUpdate后還是updated后調(diào)用。
我們?cè)诳刂婆_(tái)輸入vm.show = false。得到三者的調(diào)用順序分別為beforeUpdate、deactivated、updated。我們可以知道的是deactivated函數(shù)的觸發(fā)時(shí)間是在視圖更新時(shí)觸發(fā)。因?yàn)楫?dāng)視圖更新時(shí)才能知道keep-alive組件被停用了。
3.3、beforeDestroy和destroyed鉤子函數(shù)間的生命周期
現(xiàn)在我們對(duì)Vue實(shí)例進(jìn)行銷毀,調(diào)用app.$destroy()方法即可將其銷毀,控制臺(tái)測(cè)試如下:
我們發(fā)現(xiàn)實(shí)例依然存在,但是此時(shí)變化已經(jīng)發(fā)生在了其他地方。
beforeDestroy鉤子函數(shù)在實(shí)例銷毀之前調(diào)用。在這一步,實(shí)例仍然完全可用。
destroyed鉤子函數(shù)在Vue 實(shí)例銷毀后調(diào)用。調(diào)用后,Vue 實(shí)例指示的所有東西都會(huì)解綁定,所有的事件監(jiān)聽器會(huì)被移除,所有的子實(shí)例也會(huì)被銷毀(也就是說(shuō)子組件也會(huì)觸發(fā)相應(yīng)的函數(shù))。這里的銷毀并不指代'抹去',而是表示'解綁'。
銷毀時(shí)beforeDestory函數(shù)的傳遞順序?yàn)橛筛傅阶樱琩estory的傳遞順序?yàn)橛勺拥礁浮?/p>
4、一些應(yīng)用鉤子函數(shù)的想法在created鉤子中可以對(duì)data數(shù)據(jù)進(jìn)行操作,這個(gè)時(shí)候可以進(jìn)行ajax請(qǐng)求將返回的數(shù)據(jù)賦給data。
雖然updated函數(shù)會(huì)在數(shù)據(jù)變化時(shí)被觸發(fā),但卻不能準(zhǔn)確的判斷是那個(gè)屬性值被改變,所以在實(shí)際情況中用computed或match函數(shù)來(lái)監(jiān)聽屬性的變化,并做一些其他的操作。在mounted鉤子對(duì)掛載的dom進(jìn)行操作,此時(shí),DOM已經(jīng)被渲染到頁(yè)面上。在使用vue-router時(shí)有時(shí)需要使用<keep-alive></keep-alive>來(lái)緩存組件狀態(tài),這個(gè)時(shí)候created鉤子就不會(huì)被重復(fù)調(diào)用了,如果我們的子組件需要在每次加載或切換狀態(tài)的時(shí)候進(jìn)行某些操作,可以使用activated鉤子觸發(fā)。所有的生命周期鉤子自動(dòng)綁定this
上下文到實(shí)例中,所以不能使用箭頭函數(shù)來(lái)定義一個(gè)生命周期方法(例如created: () => this.fetchTodos()
)。這是導(dǎo)致this指向父級(jí)。
5、 小結(jié)
加載渲染過(guò)程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子組件更新過(guò)程
父beforeUpdate->子beforeUpdate->子updated->父updated
父組件更新過(guò)程
父beforeUpdate->父updated
銷毀過(guò)程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
總結(jié)
以上所述是小編給大家介紹的Vue父子組件生命周期執(zhí)行順序及鉤子函數(shù),希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!
相關(guān)文章
Vue.sync修飾符與$emit(update:xxx)詳解
這篇文章主要介紹了Vue.sync修飾符與$emit(update:xxx),實(shí)現(xiàn)思路非常簡(jiǎn)單,文章介紹了.sync修飾符的作用和使用.sync修飾符的寫法,實(shí)現(xiàn)代碼簡(jiǎn)單易懂對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11VUE3中h()函數(shù)和createVNode()函數(shù)的使用解讀
這篇文章主要介紹了VUE3中h()函數(shù)和createVNode()函數(shù)的使用解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08如何使用vue開發(fā)公眾號(hào)網(wǎng)頁(yè)
因?yàn)轫?xiàng)目需要,近期做了一個(gè)公眾號(hào)網(wǎng)頁(yè)開發(fā)。在此期間也踩了一些坑,解決這些坑之后,準(zhǔn)備對(duì)這個(gè)項(xiàng)目進(jìn)行復(fù)盤。記錄下項(xiàng)目從開發(fā)到上線所解決的問(wèn)題,并對(duì)使用vue進(jìn)行公眾號(hào)開發(fā)的步驟進(jìn)行一個(gè)總結(jié)。方便以后有問(wèn)題進(jìn)行查閱。希望對(duì)你有所幫助2021-05-05vue2.0實(shí)現(xiàn)選項(xiàng)卡導(dǎo)航效果
這篇文章主要為大家詳細(xì)介紹了vue2.0實(shí)現(xiàn)選項(xiàng)卡導(dǎo)航效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03ant-design-vue中設(shè)置Table每頁(yè)顯示的條目數(shù)量方式
這篇文章主要介紹了ant-design-vue中設(shè)置Table每頁(yè)顯示的條目數(shù)量方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10vue中利用prop進(jìn)行父子通信時(shí)的注意事項(xiàng)總結(jié)
這篇文章主要給大家介紹了關(guān)于vue中利用prop進(jìn)行父子通信時(shí)的注意事項(xiàng),文中通過(guò)實(shí)例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01