理解mpvue的生命周期
mpvue是美團基於vue開發的一個開發小程序的框架,從而以vue的語法來開發小程序。在生命周期上,mpvue同時支持了vue的生命周期和小程序的生命周期,這可能讓新上手的同學費解。這篇文章就來講講mpvue的混合生命周期。
一個bug
在深入之前,先說一個mpvue開發中的常見的坑。假設你在一個頁面中有打開了一個彈窗,然後在沒有關閉的情況下返回上一頁,然後再重新進入時,彈窗依然在那裏。按道理說,返回上一頁時,彈窗所在的page實例已經被unload了,為什麽再次進入彈窗還在呢,這其實是mpvue混合生命周期搞的鬼。
mpvue的生命周期
在理解mpvue的生命周期前,我們先看一下官方給出的一張圖:
不得不說這張圖做的真心有點糙,mpvue本來就不需要傳el這個option,結果還是出現在生命周期圖示中。。。將el換成page實例倒是更貼切一些。相信不少同學對mpvue的生命周期還是有點暈的。下面我們重新梳理一下各個鉤子在什麽時候觸發。
這裏以官方的mpvue/mpvue-quickstart
項目來演示小程序的生命周期函數到底在什麽時候觸發,項目代碼可以參考官方教程獲得。
console.log
研究觸發過程的做法簡單粗暴,就是直接在各個生命周期函數下打console.log
。mpvue-quickstart有3個頁面,分別是index,counter和logs,下面我們給這3個頁面加上如下生命周期函數:
created() {
console.log(‘page index created‘, this)
},
mounted() {
console.log(‘mounted‘, this)
},
onLoad() {
console.log(‘page index onLoad‘, this)
},
onShow() {
console.log(‘onShow‘, this)
},
onUnload() {
console.log(‘onUnload‘, this)
},
onHide() {
console .log(‘onHide‘, this)
},
完成後,重新編譯小程序,控制臺打印的日誌如下圖,分析可以看出以下結論:
vue的created函數先於小程序onLoad函數調用
一個頁面對應一個vue實例,app也對應一個vue實例(打印出的this)
在小程序的第一頁出現前,已經創建出了所有頁面對應的Vue實例。看下圖可以看出,在index頁面的onLoad觸發前,page counter,logs的created函數已經觸發。
有同學可能會以為vue的實例是和小程序的page實例一起出現的,然而事實並不是這樣。
vue的實例在頁面顯示之前就已經被初始化了,也就是在main.js中執行new vue(App)
的時候。而小程序的頁面則是按需加載的,當用戶跳轉到那個頁面時,page實例才會創建。這個觀點同樣可以由vue實例下面的$mp
屬性說明,這個屬性關聯了vue的實例和小程序頁面的實例。
$mp屬性
這個屬性在原生vue中是沒有的,它是由mpvue擴展出的屬性,可以類比原生vue中的$el。在網頁中,$el指向vue驅動的dom元素。而在小程序中,$mp指向vue驅動的page或者app實例。
下面我們看看index和counter兩個頁面對應的vue實例下$mp
屬性的區別:
當頁面load後,$mp為ready狀態,並且有page屬性指向這個頁面實例,而頁面還沒有load時,$mp屬性為register狀態,沒有page屬性。也就是說,空有vue實例,但是並沒有page可以給他操作。
onUnLoad方法調用後,相應頁面vue實例還在嗎?
上面是在onUnload生命周期函數下打印的this,可見this也就是vue實例並沒有被銷毀,而小程序的page實例被銷毀了。所以vue內部的狀態會被保存,如彈窗打開關閉的狀態。假設由this.showModal控制
當頁面再次被打開時,如果this.showModal為true,頁面剛出現時彈窗就還是打開的。這就解釋了文章一開頭的那個問題。所以,如果你希望當頁面被關閉時,狀態不被保存的話,需要在onUnload方法下處理相關邏輯:
// ...
onUnload(){
this.showModal = false
}
// ...
總結:盡管mpvue同時支持了小程序的生命周期和vue的生命周期,但是兩者之間並不是共生共滅的關系
理解mpvue的生命周期