《微信小程序七日談》系列文章:
- 第一天:人生若只如初見;
- 第二天:你可能要拋棄原來的響應式開發思維;
- 第三天:玩轉Page組件的生命周期;
- 第四天:頁面路徑最多五層?導航可以這么玩;
- 第五天:你可能要在登錄功能上花費大力氣;
- 第六天:小程序devtool隱藏的秘密;
- 第七天:不要撿了芝麻丟了西瓜
本系列的文章並非初學教程,而是筆者在具體開發過程中遇到的問題以及部分解決方案。
前兩篇文章 第一天:人生若只如初見和第二天:你可能要拋棄原來的響應式開發思維零零散散地記錄了一些微信小程序的細節,主要集中在UI方面。其中提到的解決方案只是筆者自身的一些探索,並非最佳實踐,甚至不是筆者項目中最后采用的方案(最終方案會在后續文章里詳細講述)。其實小程序的UI開發並非簡短的兩篇文章可以概括的,還有許多細節待挖掘,奈何項目排期緊張,暫時就不去研究與當前需求無關的東西了。
今天這篇文章簡單記錄一下在使用小程序Page組件時對於其生命周期的一些使用心得。
鈎子函數的命名技巧
官方文檔對Page的生命周期的介紹簡單明了,在生命周期的不同階段拋出的鈎子函數依次為: onLoad
-> onShow
-> onReady
-> onHide
-> onUnload
。
使用過React的開發者肯定會對用on
做為鈎子函數命名前綴非常不舒服,React使用will、did、should等一系列有時態語義的詞匯命名鈎子函數,令開發者一眼就能分辨鈎子函數對應的生命周期階段。但是on
是具有一定歧義的。瀏覽器的用戶行為事件機制,以及我們所熟悉的jQuery中,使用on作為捕獲/監聽事件的API命名,這種情形下可以把on理解為當某件事情發生時做某些行為,這也是大部分前端工程師對on語義的理解。這種理解的on有一層攔截的意思,比如onclick
先於<a>
的href
跳轉,可以攔截其默認的click行為,在默認click之前發生。但是請大家仔細思考一下window.onload
中on
的含義。onload
的觸發時機是在文檔加載完成之后,在執行我們定義的onload邏輯之前,文檔已經完成了load行為。也就是說,onload
並沒有攔截load行為,而是在load事件之后發生。所以,on
這個詞匯並不能精准的形容到底是前還是后,它是沒有時態語義的。
具體到Page的生命周期鈎子函數,大家請憑第一感覺理解下面幾個函數的執行時機:
onLoad
onShow
onReady
我相信大部分人對於這三者的理解是:鈎子函數在load/show/ready完成之后執行。跟window.onload
是一樣的。那么請大家在思考下列兩個的執行時機:
onHide
onUnload
跟前三者是一樣的嗎?
我們先不去探究后兩者與前三者的執行時機策略是否相同。請大家先以常規的思維思考下列的應用場景:app導航欄左上角有個“返回”按鈕,如下圖:
很常見的一個邏輯是:如果用戶在未保存表單數據之前點擊返回按鈕的話,通常會彈出一個提示層,如下:
也就是說,頁面有個beforeUserLeave
的策略,在執行userLeave
之前進行攔截並給出提示,以免用戶的操作失誤。這不僅僅是業務邏輯的需求,也是一個網站從開始到被關閉過程中的一環,這就是我們熟知的window.onbeforeunload
事件。
同樣,筆者參與的項目也有上述的業務邏輯,在用戶離開頁面之前提示用戶的編輯狀態。對應小程序的幾個鈎子函數,結合React和Vue的開發經驗,自然而然地就想到在onHide
或者onUnload
內攔截返回操作並給出提示。
但是,並不行!onHide
和onUnload
的執行時機策略竟然跟onLoad/onShow/onReady
一樣!
也就是說,在page被卸載之后才會執行onUnload
。這就造成用戶點擊返回按鈕,已經回到了上一個頁面,然后,突然彈出了一個提示框:
用戶:WTF?
鈎子函數的正確執行時機
其實官方文檔詳細展示了Page的各個鈎子函數的執行時機,如下圖:
從上圖中可以看出:
onHide
是在當前Page被“set to background”之后觸發;onUnload
是在當前Page被“destory”之后觸發。
當然,每個人設計組件時對組件的生命周期都有自己的理解和實現,並不是說小程序的Page生命周期設計的不好,只是希望能夠提供更細化的鈎子函數,比如上文提到的“before”策略,以便實現更人性化的用戶體驗。
data全部動態化
vue.js的1.x版本提供了activate鈎子函數,這個鈎子阻塞了組件的后續執行,方便開發者在組件渲染之前進行特殊處理,比如使用jsonp請求數據,成功后執行done()
觸發組件的后續流程。
小程序里有沒有阻塞的鈎子函數呢?
可能大部分人跟筆者一樣,第一個想法就是試試onShow
是否是阻塞的,但是結果並不像預期的那樣。小程序的Page組件沒有提供阻塞的鈎子函數,根據上文中的官方配圖可以看到,在組件的data更新之后有個"Rerender"
動作。Page組件的數據統一為data
,而不是像React或者Vue區分props
和state/data
。這種設計的優點是不用特意的對某個data進行監聽,data全部是動態的,這意味着任何一個data的改變都會觸發Rerender。
小程序提供一些內置的UI組件,但是邏輯組件只有app
和page
兩種,並且兩者並不是嚴格的父子組件關系。所以,page
組件並不需要類似React中的props
數據,所有的數據都屬於自身。
總結
Page組件的生命周期十分簡潔,上手容易。但是面對一些特殊需求時並不能提供很好的支持。這種情況下我們不得不適當地修改需求邏輯。
小程序中並沒有父子組件的關系譜,組件的數據不會區分props
和state
,全部是統一的data
,並且全部是動態的。任何data的修改都會觸發Rerender。
最近發現有些網站、個人博客以及微信公眾號未經授權轉載了筆者的文章,作為技術人員,希望大家都具有基本的職業道德。剽竊的技術是不會得到尊重的,對於未經授權的轉載方,必要的時候會付諸法律手段。