轉自IMWeb社群,作者:llunnn,原文連結
最近在開發小程式,嘗試性地使用了一下mpvue框架。
mpvue 是一個使用 Vue.js 開發小程式的前端框架。框架基於 Vue.js 核心,mpvue 修改了 Vue.js 的 runtime 和 compiler 實現,使其可以執行在小程式環境中,從而為小程式開發引入了整套 Vue.js 開發體驗。
mpvue同時維護了Vue和小程式的兩套機制,因此需要對兩套機制進行關聯。這裡主要對mpvue的生命週期來進行一些梳理。
微信小程式生命週期
首先我們需要了解,微信小程式的生命週期:
App物件,主要有onLaunch, onShow和onHide。
Page物件,主要有onLoad, onShow, onReady, onHide和onUnload。
Vue到mpvue
mpvue的出現使得我們可以用書寫Vue例項的方式去宣告這兩種物件,並使得Vue例項相容小程式的生命週期。
Vue的生命週期主要體現在8個鉤子:beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed。
來對比一下Vue和mpvue的生命週期,看一下mpvue做出了什麼改變:
對比來看,mpvue主要是對created和beforeMount之間的過程做了改變。
在Vue中,這個階段主要作用是將template編譯為render函式:
而在mpvue中,對於App或Page元件(這裡的Is App or Page component應該要解釋為“是否為App或Page元件”),為他們初始化小程式的生命週期,並註冊App物件或Page物件:
這裡可以看出來,mpvue中,Vue和小程式生命週期鉤子觸發的基本順序是beforeCreate -> created -> onLaunch/onLoad -> onShow -> onReady -> beforeMount -> mounted -> ...
實踐驗證
這裡有一個入口頁面,包含一個通過wx.navigateTo跳轉到newPage的按鈕。
newPage中包含一個card元件,和一個通過wx.navigateBack跳轉回入口頁面的按鈕。
在App, newPage和card的各個生命週期鉤子輸出資訊,來觀察它們的觸發情況和順序。
在App被建立,跳轉到newPage前
我們可以觀察到,app物件首先被建立,觸發onLaunch和onShow。
在這之後,newPage被create。需要注意的是,此時我們還沒有跳轉到newPage,也就是說在mpvue中,無論頁面是否被訪問到,其Vue例項的beforeCreate和created都在app建立後就被觸發。
第一次跳轉到newPage並返回入口頁面
由於newPage頁面的beforeCreate和created已經提前被觸發過了,在呼叫了wx.NavigateTo跳轉到newPage時,先觸發小程式的生命週期,再觸發beforeMount,這時候開始建立子元件card的例項,按照beforeCreate -> created -> onLoad -> onReady -> beforeMount -> mounted 的順序觸發生命週期鉤子。(這裡元件的onShow為什麼沒有觸發..需要再深入探究一下)
在wx.navigateBack時,小程式的生命週期鉤子onUnload被觸發。但需要注意的是:Vue的生命週期鉤子beforeDestroy和destroyed並沒有被觸發,也就是說小程式中newPage的page物件被解除安裝了,但newPage和card的Vue例項並沒有被銷燬。
第二次跳轉到newPage並返回入口頁面
newPage和card都已經被create且沒有destroy,在再次wx.navigateTo時將直接從onLoad -> onShow -> onReady開始觸發,newPage的mount和update過程也會出發,而component之後update過程被觸發了。這裡可以發現,在onLoad之後還經過了幾個階段,才開始觸發Vue例項的生命週期鉤子,而上一次儲存在記憶體中的資料並沒有被destroy,因此在重新載入的過程中,Vue例項還儲存著上一次載入頁面時的資料。
開發時遇到的問題
遇到的問題主要是由create過程在頁面載入前就被統一觸發引起的。 在使用Vue時,經常在created鉤子中獲得新的data。因為此時對data的資料觀測已經被建立,但是頁面內容尚未被掛載,Vue例項可以觀測到data的變化並在檢視顯示出來之前改變其內容。
如果在mpvue中,我們想獲取頁面路由query中的資料,或是想在頁面建立時請求介面,我們可能會這樣考慮:
在created中獲取資料? 在mpvue中,created只被觸發一次,且在頁面建立前被觸發,也就是說query中的資料是無法獲得的,再次訪問頁面時如果資料發生了變化,created中的邏輯也並不會再次執行。
推遲到beforeMount? 從功能上說,在beforeMount獲取資料是沒有問題的。但由於頁面unload時沒有觸發destroy,在再次載入頁面時,Vue例項仍然儲存著前一次獲得的資料,而頁面的onLoad、onShow均在beforeMount之前被觸發,實踐時會發現,頁面在資料更新之前就會被顯示出來,舊的資料會在頁面中“一閃而過”。
在onLoad中獲取資料?
實踐證明這的確是一種最穩妥的方法,資料能被正確地設定,頁面也不會“閃”。
但是官方文件有這樣一句話:
除特殊情況外,不建議使用小程式的生命週期鉤子。
這裡大概是為了程式碼的移植性做考慮吧,不知道這裡算不算特殊情況呢。
使用computed? 為了避免使用小程式的生命週期鉤子,還可以考慮使用computed的來獲取query中的內容,而query需要在頁面onLoad之後才存在,這裡需要注意做一些判斷。
總結
從Vue過度到mpvue還是非常平滑的,特別是在有過小程式開發經驗的情況下。但是由於小程式本身和瀏覽器的差異,使得開發過程中會遇到一些難以理解的問題,將生命週期做一下梳理對更順利地進行開發是有一些好處的。
但是,從這裡也可以看到,對於開發小程式來說,mpvue實際上額外地維護了一套Vue的機制,並對小程式的事件、資料進行代理、同步,實際上這個過程可能會造成一些效能上的損耗。再加上mpvue目前還是存在一些缺陷,而小程式也支援了資料繫結、元件化開發,個人認為若是追求高質量的開發還是直接使用原生小程式更優吧~