導讀
小程式科普類的文章已經很多了,今天這裡講的是針對小程式的優化方法,可以有效提高小程式的響應速度和使用者體驗。當然,開發體驗也提高不少。
1、提高頁面載入速度
在小程式這個環境下,怎樣提高頁面載入速度呢? 這個問題很大,我把問題具體一下,如何縮短從使用者點選某個連結,到開啟新頁面的這段時間? 這裡拋一個核心關鍵點:
從頁面響應使用者點選行為,開始跳轉,到新頁面onload事件觸發,存在一個延遲,這個延遲大概在100-300ms之間(安卓響應比ios慢些)。
這個延遲說短不短,我們可以利用這段時間,預先發起新頁面所需要的網路請求。這樣一來,就節省了100-300ms(或者一個網路請求的時間)。
知道有這個gap後,程式碼如何實現呢?
說白了,就是實現一個在A頁面預載入B頁面資料的功能。但而這種跨頁面的呼叫,很容易把邏輯搞複雜,將不同頁面的邏輯耦合在一起。所以,我們希望將預載入的邏輯隱藏於無形中,不增加任何的頁面間耦合,以及開發複雜度。
下面以騰訊視訊小程式為例,講解下技術實現。
小程式首頁:
當使用者點選海報圖後,會執行以下程式碼(就一行):
接下來程式會載入播放頁:
播放頁主要程式碼:
可以看到,不管是外部頁面的呼叫還是實際邏輯的實現都非常簡潔。在第二個頁面中,我們擴充套件了Page的生命週期函式,增加了onNavigate方法。該方法在頁面即將被建立但還沒開始建立的時候執行。
老司機也許會發現這裡有點蹊蹺。在首頁點選的時候,播放頁根本就沒有建立,物件都不存在,怎麼訪問到裡面的方法呢?
這裡就要說下微信的頁面機制。
在小程式啟動時,會把所有呼叫Page()方法的object存在一個佇列裡(如下圖)。每次頁面訪問的時候,微信會重新建立一個新的物件例項(實際上就是深拷貝)。
也就是說,在A頁面在執行點選響應事件的時候,B頁面的例項還沒建立,這時候呼叫的onNavigate方法,實際上是Page物件的原型(小程式啟動時候建立的那個)。
而接下來馬上要被建立的B頁面,又是另外一個object。所以,在onNavigate和onLoad方法中,this指標指的不是同一個物件,不能把臨時資料儲存在當前object身上。因此我們封裝了一對全域性的快取方法,put()和take()。
為了通用性,Page上用到的公共的方法,比如route、put、take都定義在了一個Page的基類裡面。基類還同時保存了所有頁面的list,這樣就可以做到根據頁面名調用具體頁面的onNavigate方法。當然,並不是每個頁面都需要實現onNavigate方法,對於沒有定義onNavigate方法的,route函式會跳過預載入環節,直接跳轉頁面。所以對於開發者來說,不需要關心別的頁面實現了什麼,對外看來完全透明。
2、使用者行為預測
在上面的例子中,我們實現了使用者主動點選頁面,提前載入下一頁面資料的方法。而在某些場景下,使用者的行為可以預測,我們可以在使用者還沒點選的時候就預載入下個頁面的資料。讓下個頁面秒開,進一步提升體驗的流暢性。
繼續以騰訊視訊小程式為例,主介面分為3個頁卡(大部分小程式都會這麼設計),通過簡單的資料分析,發現進入首頁的使用者有50%會訪問第二個頁卡。所以預載入第二個頁卡的資料可以很大程度提高使用者下個點選頁面的開啟速度。
同樣,先看看程式碼實現。 首頁預載入頻道頁的姿勢:
頻道頁的實現方法:
跟第一個例子類似,這裡定義了一個preLoad()方法,同時給Page擴展了一個onPreload事件。頁面調用preLoad()後,基類會自動找到該頁面對應的onPreload函式,通知頁面執行預載入操作。 跟第一個例子不同,這裡預載入的資料會儲存在storage內,因為使用者不一定會馬上訪問頁面,而把資料存在全域性變數會增加小程式佔用的記憶體。微信會毫不猶豫的把記憶體佔用過大的小程式給殺掉。
也許對於大部分有app開發經驗的同學來說,更普遍的做法是先讓頁面展示上次快取的資料,再實時拉取新資料,然後重新整理頁面。這個方法在小程式上也許體驗並不太好,原因是小程式的效能以及頁面渲染速度都不如原生app。將一個大的data傳輸給UI層,是一個很重的操作。因此不建議採用這種方法。
3、減少預設data的大小
剛剛說到,頁面開啟一個新頁面時微信會深拷貝一個page物件,因此,應該儘量減少預設data的大小,以及減少物件內的自定義屬性。有圖有真相:
以一個100個屬性的data物件為測試用例,在iphone6上,頁面的建立時間會因此增加150ms。
4、元件化方案
微信沒有提供小程式的元件化方案(相信一定在實現中)。但開談不說元件化,寫再多程式碼也枉然。這裡演示一個簡單的元件化實現。
以騰訊視訊播放頁為例,頁面定義如下:
其中,P()函式是自定義的基類。這是一個非常有用的東西,可以把所有通用的邏輯都寫在基類裡面,包括pv統計,來源統計,擴充套件生命週期函式,實現元件化等。
函式第一個引數是頁面名稱,作為頁面的key。第二個是page物件,其中擴充套件了一個comps陣列,裡面就是所有要載入的元件。
以播放器元件/comps/player/index.js為例:
元件的定義跟一個普通Page物件一模一樣,有data屬性,onLoad、onShow等事件,也有頁面響應的回撥方法。wxml模板裡定義的事件和js事件一一對應。
基類做的事情,就是把這些元件物件的屬性和方法複製到Page物件上(淺拷貝)。其中data屬性會merge到一起。而微信預定義的生命週期函式(包括自己擴充套件的),則封裝成佇列按序執行。比如當系統呼叫onLoad方法時,實際上是執行了所有元件的onLoad方法,最後再執行Page的onLoad。
以上是程式碼部分,至於wxml模板和wxss部分,就要手工import過去了。
wxml:
wxss:
5、其他
雖然小程式已經足夠小巧,但啟動速度還是有那麼2-3秒,無法做到秒開。樓主嘗試對小程式的啟動時間做優化,但沒有找到多少有價值的優化點。單個頁面的初始化只需要1-2ms。也許大部分時間消耗在了微信跟伺服器端通訊的過程中。
所幸,騰訊提供了一個可以自主進行伺服器效能測試的環境,使用者只需要填寫域名和簡單的幾個引數就可以獲知自己的伺服器效能情況,目前在騰訊WeTest平臺可以免費使用。