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)
},
onReady () {
console.log('page index onReady', 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函數已經觸發。
-
mounted會在onReady之后觸發,這個鈎子適用於當頁面出現時執行一些過渡效果的情況。

有同學可能會以為vue的實例是和小程序的page實例一起出現的,然而事實並不是這樣。
vue的實例在頁面顯示之前就已經被初始化了,也就是在main.js中執行new vue(App)的時候。而小程序的頁面則是按需加載的,當用戶跳轉到那個頁面時,page實例才會創建。這個觀點同樣可以由vue實例下面的$mp屬性說明,這個屬性關聯了vue的實例和小程序頁面的實例。
Tips: 當你需要使用wx.createSelectorQuery() 查詢某個元素的位置時,應當放在onReady或者mounted的生命周期中進行,保證位置的准確性
$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控制
data(){
return {
showModal: false// 彈窗為關閉狀態
}
}
當頁面再次被打開時,如果上次頁面this.showModal為true,則頁面剛出現時彈窗就還是打開的。這就解釋了文章一開頭的那個問題。所以,如果你希望當頁面被關閉時,狀態不被保存的話,需要在onUnload方法下處理相關邏輯,或者在onShow時重置data里的狀態
// ...
onUnload(){
this.showModal = false
}
// 更通用一點
onUnload() {
Object.assign(this, this.$options.data())
}
總結:盡管mpvue同時支持了小程序的生命周期和vue的生命周期,但是兩者之間並不是共生共滅的關系。更多閱讀可以參考這個官方倉庫的issue-同一路由切換時,上一次的頁面數據會保留 #140
