理解生命週期和鉤子函數的調用在工程中十分重要,關於Vue生命週期的理解我推薦這一遍詳解Vue生命週期 。寫得真不錯,淺顯易懂,任何初學者一看都能明白是怎麼一回事。今天我當然不是來講生命週期的,我要將的是嵌套組件的創建順序,嵌套組件件鉤子函數的執行順序,最後是銷燬順序。閒話少說,直接上代碼。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>測試Vue</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app" style="border: 1px solid red;padding: 10px">
<div >
根組件
<button @click="handleClick">根組件銷燬</button>
</div>
<a-component></a-component>
<b-component></b-component>
</div>
<script>
Vue.component('a-component' , {
template : `
<div style="border:1px solid black;padding: 10px;margin: 10px">
組件A
<c-component/>
</div>
` ,
data(){
return {
name : 'a-component'
}
} ,
beforeCreate(){
console.log('A組件beforeCreate')
} ,
created(){
console.log('A組件created')
} ,
beforeMount(){
console.log('A組件beforeMount')
} ,
mounted(){
console.log('A組件被mounted')
} ,
beforeDestroy(){
console.log('A組件被beforeDestroy')
} ,
destroyed(){
console.log('A組件被destroyed')
}
});
Vue.component('b-component' , {
template : `
<div style="border: 1px solid black;padding: 10px">組件B</div>
` ,
data(){
return {
name : 'b-component'
}
} ,
beforeCreate(){
console.log('B組件beforeCreate')
} ,
created(){
console.log('B組件created')
} ,
beforeMount(){
console.log('B組件beforeMount')
} ,
mounted(){
console.log('B組件被mounted')
} ,
beforeDestroy(){
console.log('B組件被beforeDestroy')
} ,
destroyed(){
console.log('B組件被destroyed')
}
});
Vue.component('c-component' , {
template : `
<div style="border:1px solid black">組件C</div>
` ,
data(){
return {
name : 'c-component'
}
} ,
beforeCreate(){
console.log('C組件beforeCreate')
} ,
created(){
console.log('C組件created')
} ,
beforeMount(){
console.log('C組件beforeMount')
} ,
mounted(){
console.log('C組件被mounted')
} ,
beforeDestroy(){
console.log('C組件被beforeDestroy')
} ,
destroyed(){
console.log('C組件被destroyed')
}
});
let vm = new Vue({
el : '#app' ,
computed : {} ,
methods : {
handleClick(){
this.$destroy();
}
} ,
beforeCreate(){
console.log('根組件beforeCreate')
} ,
created(){
console.log('根組件created')
} ,
beforeMount(){
console.log('根組件beforeMount')
} ,
mounted(){
console.log('根組件被mounted')
} ,
beforeDestroy(){
console.log('根組件被beforeDestroy')
} ,
destroyed(){
console.log('根組件被destroyed')
}
})
</script>
</body>
</html>
在代碼裏我註冊了三個全局組件,分別是a-component,b-component和c-component,另外還有一個根組件vm作爲入口。
其中根組件下有一個按鈕、a和b兩個組件,a組件下又有c組件。下面來看執行結果:
由上面打印的結果,我們可以知道,同一個組件中的beforeCreate,created和beforeMount是按順序執行,並不會因爲嵌套組件而打斷。這三個鉤子的執行順序是先外後內,先是根組件的created先執行,接着A組件先執行,意料之中,但接下來C組件先執行created就有點意外了,我們可以認爲C組件是A組件的一部分,所以A組件創建的時候也會創建C組件。最後B組件的created執行。另外需要注意,在嵌套組件中,同一個組件beforeMount和mounted不是在一起執行的。接下來看一下掛載順序,沒接觸過的人肯定會感到很驚奇,順序和創建順序完全反過來了。這是怎麼回事呢?這個需要知道beforeMount鉤子到mounted鉤子之間發生了什麼。不清楚可以看一下我上面發的鏈接,裏面清楚地講到,mounted之後,el中的{{name}}類似的語句和指令都會被替換成真正的文本,也說明mounted之後所有的模板被渲染成DOM了。大家都知道Vue和React中都有virtual dom(虛擬dom),虛擬dom是什麼,按我的理解是按模板的結構使用vnode組織的對象,說白了就是一個對象。模板是被編譯成vdom之後纔會被渲染成真正dom,也就是說mounted之後vdom肯定已經存在了,那麼也就是說mounted的執行順序跟dom的組織有關,像c組件在A組件中,所以必須先有c組件的vdom纔能有A組件的Vdom。同理,根組件也是如此,先編譯完成a和b組件的vdom才能生成根組件的vdom。這類似遞歸,要想執行完成遞歸函數,必須等最底部的先完成,然後上面的才能一一執行完成。
接下來,看一下銷燬順序:點擊一下銷燬按鈕
是不是部分猜到,然後關於beforeDestroy部分沒有猜到。這又得看beforeDestroy和destroyed的區別,還是在生命週期那篇文章中。在執行beforeDestroy時,實例還是存在的,即組件裏面的所有數據都是可以使用的,到destryed執行的時候,一切都已被銷燬了,不可以使用組件裏面的所有東西。合理的毀壞方法,我們可以想到,先把最裏面的毀了,再逐步往外銷燬。
總的來說,鉤子的執行順序肯定是由vue的作者決定,但這一切又在情理之中,可以說這是一種必然的選擇。