Vue生命週期,及父子組件生命週期順序

祭出官方圖: vue

 

 

 Vue組件的生命週期主要爲8個: beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。ajax

其實官方圖已經講明白不少了。包括各個階段之間作了些什麼事情,但在掌握上終歸仍是有一些模糊的地方。api

下面作了一些梳理。數組

我的理解是主要分爲三個階段:app

  • 建立階段(註冊實例與掛載): beforeCreate、created、beforeMount、mounted
  • 運行階段:beforeUpdate、updated
  • 註銷階段:beforeDestroy、destroyed

如下代碼vue分別註冊了father和child組件,並在father中調用了child子組件來觀察他們的生命週期已經前後關係:dom

<!-- HTML部分 -->
<!-- 父組件掛載 -->
<div id="app"></div>

<!-- child模板 -->
<div class="child" @click="updateTest()">
    i'm child <br />
    data: {{data}}
</div>

<!-- father模板 -->
<div class="father">
        <button @click="changeDate()">{{tData}}</button>
        <button @click="destroyEl()">destroy</button>
        <child v-if="showChild"></child>
        I'm father
</div>

 

 

/*JavaScript部分*/

/*child*/
var child = Vue.extend({
     template: '#child',
     data(){
       return {
         data: 0
       }
     },
     methods:{
        consoleinfo(){    console.log(this.data)},
        updateTest(){     this.data += 1}
     },
     beforeCreate(){    console.log('child-beforeCreate')},
     created(){        console.log('child-created')},
     beforeMount(){    console.log('child-beforeMount')},
     mounted(){        console.log('child-mounted')},
     beforeUpdate(){    console.log('child-beforeUpdate')},
     updated(){        console.log('child-updated')},
     beforeDestroy(){
         console.log('child-beforeDestroy:')
         this.consoleinfo()
     },
     destroyed(){
         console.log('child-destroyed:')
         this.consoleinfo()
     }
})   

 

/*father*/
new Vue({
    el: '#app',
    template:'#father',
    components:{child},
    data(){
         return {
             tData: 0,
             tCom: 'computed',
             showChild: true
         }
    },
    computed:{
         tComputed(){ return this.tCom}
   },
   methods:{
         consoleinfo(){
             console.log('data: ' + this.tData)
             console.log('computed: ' + this.tComputed)
             console.log('el: ' + this.$el.outerHTML)
         },
         changeDate(){
             this.tData += 1
             this.tCom = 'changeComputed'
         },
         destroyEl(){
        //this.showChild = false
             this.$destroy()
         }
   },
   /**
   建立階段
   */
   beforeCreate(){
         console.log('beforeCreate:')
         this.consoleinfo()
   },
   created(){
         console.log('created:')
         this.consoleinfo()
   },
   beforeMount(){
         console.log('beforeMount:')
         this.consoleinfo()
   },
   mounted(){
         console.log('mounted:')
         this.consoleinfo()
   },
   /**
   運行階段
   */
   beforeUpdate(){
         console.log('beforeUpdate:')
         this.consoleinfo()
   },
   updated(){
         console.log('updated:')
         this.consoleinfo()
   },
   /**
   註銷階段
   */
   beforeDestroy(){
         console.log('beforeDestroy:')
         this.consoleinfo()
   },
   destroyed(){
         console.log('destroyed:')
         this.consoleinfo()
   }
})    

 

 

接下來看下各個生命週期控制檯的輸出:函數

建立階段

beforeCreate:

在beforeCreate階段以前,vue作了init初始化的操做。操做包括:this

—— 註冊一些vue默認的屬性方法事件(例如初始化vue的生命週期 —— 鉤子beforeCreate()等)spa

所以,在beforeCreate鉤子調用時,這些默認屬性方法事件已經註冊完成,可是vue實例中的data、computed、methods等都未定義:code

—— 圖中函數consoleinfo(),報錯「not a function」’

Created:

beforeCreate與Created之間,實例將定義的data、methods等初始化並進行注入。

所以在Created階段,控制檯已經可以輸出data、computed值,包括控制檯輸出的方法methods,這些值都已在當前father組件實例中定義生成了。

—— 此時只是在js操做了vue實例,dom中的元素還並未掛載,所以實例的$el並不可以調用outerHTML,所以報錯。

——(雖然father組件調用了child組件,可是此時子組件child生命週期還未開始!這裏要注意,只是father組件作初始化。子組件實例的生命週期何時開始,接下來的father生命週期裏會涉及)

—— 因爲data數據、methods方法等在created中已被定義,此時已經能夠去作一些ajax請求後臺數據

beforeMount

created與beforeMount之間,主要作了兩步工做:

一、判斷實例在dom中有沒有掛載的元素(el:‘#app’),只有掛載了纔可以繼續。掛載好後,實例即與掛載dom元素進行了綁定(佔坑),實例中也能夠進行引用;

二、渲染dom模板。渲染dom模板只是在內存中,並不是是在HTML中的DOM結構中渲染,因此前臺在這個階段時,組件對應的元素是沒有顯示的。(在調用 this.$el.outerHTML 後,控制檯輸出 <div id="app"></div>)

—— 能夠看到fathe組件的beforeMount時,child子組件的vue建立生命週期已經完成到mounted階段。說明father在執行dom模板渲染的時候,會監測模板中是否有自定義的vue子組件。若是有,就進入子組件的生命週期的建立階段,等到全部子組件的完成建立並掛載(mounted)到父組件的模板當中後。才能代表父組件在內存中的模板渲染完成。

—— 子組件的mounted階段雖然完成,但父組件仍在beforeMounte階段時。前臺也看不見子組件渲染的效果,子組件只是完成了掛載到父組件的模板中了(控制檯能夠看到dom樹中的元素並未變化)。所以此刻在子組件的mounted階段直接調用一些方法(dom操做方法)可能會形成異常錯誤。爲保險起見可在子組件中經過 $nextTick() 回調,等下一次DOM更新後再進行dom的操做。

—— 以上內存中渲染DOM能夠了解一下Virtual DOM技術。

mounted

mounted階段表示,當前實例在內存中渲染的組件模板,已成功掛在到父組件orDOM樹中。

控制檯能夠看出DOM樹中元素已經發生變化。

—— 這個階段能夠引用到元素,上面子組件提到過,一些狀況下能夠經過 $nextTick() 回調,避免一些意外錯誤。

運行階段

beforeUpdate

經過點擊左上角的button來改變father實例中的值

控制檯

首先:只有實例中定義的變量綁定在了dom樹中,例如 <div>{{data}}<div>,當data發生改變,纔會進入beforeUpdate階段。若dom樹中未綁定某變量,該變量只在實例中發生變化,那麼就不會進入beforeUpdate階段!

我在代碼中綁定了tData變量,從beforeUpdate階段中能夠看到,內部變量tData已經變化。頁面中DOM的 tData綁定值還未變化。

子組件的數據更新,不會引發父組件的beforeUpdate和updated生命週期鉤子

update

 

beforeUpdate和updated階段直接,vue根據變量更新後的數據在虛擬DOM中進行渲染(圖中re-render)。

然後進行頁面相應組件的更新。

控制檯組件中值爲1

註銷階段

若一個組件被判斷爲銷燬,則進入相應的銷燬階段。通常狀況下銷燬階段的調用有:

  • v-if = ‘false’ 
  • v-for 列表發生了變化(列表中部分item再也不使用)
  • 實例調用 $destroy() 方法

beforeDestroy

進入該階段,代表實例已經接收到了被銷燬的指令。

在該階段,實例的屬性、方法、事件等仍然能夠調用。

destroyed

在beforeDestroy與destroyed之間,組件開始註銷本身的屬性、方法、事件以及本身的子組件。只有等到全部都已註銷完成(子組件達到destroyed階段),父組件纔可以進入destroyed階段。看控制檯。

以上註銷方法,是經過 $destroy() 來進行的,能夠看到調用該方法後,頁面上的元素並未隨vue實例註銷。可是經過點擊button已經沒法改變其button數組,說明實例已經不存在。

—— $destroy只是徹底銷燬一個實例。清理它與其它實例的鏈接,解綁它的所有指令及事件監聽器。

—— 官方建議,$destroy 在多數狀況下,仍是用 v-if 和 v-for 來替代

相關文章
相關標籤/搜索