您好,登錄后才能下訂單哦!
這篇文章主要介紹小程序性能優化示例,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
先簡單介紹一下項目,就是一個比較常規的點餐小程序。
界面如圖:
左邊是分類菜單,右邊是長列表,有多個分類的商品,單個分類滾動完后可以繼續滾動切換到下一個分類,同時左邊的分類菜單選中態會跟著切換到當前商品列表顯示的分類。
考慮到更好的用戶體驗,以及參考了美團等點餐小程序,這個商品列表的數據是一次性返回的。目前遇到的問題就是,當商品數量比較多時,首次渲染時間很長,而且頁面會卡頓。
小聲bb:其實就是原來代碼(由于歷史原因)寫得太爛了……OTL
先放個圖
小聲bb:連小程序都看不下去了,要警告了
微信開發者工具都有警告了,而且提示里面也有定位到具體代碼的位置,所以關鍵就是這個 setData
!!!
我們可以先看看官方對于小程序性能以及 setData
優化的一些建議。(developers.weixin.qq.com/miniprogram…)
具體實踐:
setData
不能一次性傳太多數據,如果列表太長,可以分開渲染【比如轉化為二維數組,每次循環渲染一個數組】。v1:簡單粗暴版
// 每次渲染一個分類// 假設goodsList是一個二維數組goodsList.forEach((item, index) => { this.setData({ [`goodsList[${index}]`]: item }) })復制代碼
像上面這樣寫會有一個問題,頁面首屏渲染是快了,但是點擊頁面操作(比如加購按鈕等),頁面會卡住,等一下才有反應,操作反饋延遲嚴重。
其實這是因為,這個循環是把單次 setData
數量減少了,但是卻變成了循環多次 setData
,我們看著首屏顯示好了,但是其實其他分類(其他數組)還在渲染,線程還是忙碌狀態,JS 線程一直在編譯執行渲染,點擊事件不能及時傳遞到邏輯層,邏輯層亦無法及時將操作處理結果及時傳遞到視圖層。
v2:定時器hack版
既然js線程忙著渲染,那我們可以強制讓它先停下來。于是有了v2的定時器hack版。
// 每次渲染一個分類let len = data.goodsList ? data.goodsList.length : 0;let idx = 0let timer = setInterval(() => { if (idx < len) { that.setData({ [`goodsList[${idx}]`]: data.goodsList[idx] }); idx++ } else { clearInterval(timer) } }, 15);復制代碼
現在首屏渲染速度問題解決了,點擊按鈕延遲響應問題也解決了。就是代碼有點hack,逼死強迫癥
v3:大殺器——虛擬列表
虛擬列表簡單說原理就是只渲染當前顯示屏幕區域以及前n屏和后n屏的數據,用一個單獨的字段保存當前需要顯示的數組(就是當前一屏+前n屏+后n屏),每次列表滾動的時候重新計算需要顯示的數據,更新這個字段,頁面就會相應更新了。這樣就能保證頁面上的元素節點數量不會太多,就可以支持大量數據的長列表需求。
更詳細的原理和實現各位同學們可以自己搜一下,此處不展開。
小程序官方也有開源的虛擬列表組件:recycle-view
setData
可以支持顆粒更新,指定到具體的屬性。比如加購等操作,需要更新商品右上角的小數字,可以這樣寫:
this.setData({ [`goodsList[${categoryIndex}][${goodsIndex}].num`]: goodsItem.num })復制代碼
data
,不要用 setData
更新,因為 setData
會觸發頁面渲染。eg:
Page({ data: { ... }, // 跟頁面渲染無關的數據 state: { hasLogin: false, }, ... })// 更新的時候直接賦值就行this.state.hasLogin = true復制代碼
PS:或者甚至不需要掛載到 page
對象下,直接用普通變量保存。
在長列表中圖片大小如果不加限制,大量的大圖會占用很多內存,有可能導致iOS客戶端內存占用上升,從而觸發系統回收小程序頁面。除了內存問題外,大圖片也會造成頁面切換的卡頓。
解決辦法就是根據當前顯示的圖片區域大小,取尺寸剛好合適(2倍-3倍圖)的圖片。
建議圖片用CDN,一般CDN服務廠商提供圖片服務的都會提供裁剪圖片的接口,然后接口只返回原圖鏈接,前端根據需要傳參數裁剪圖片。前端具體做法可以寫公共的圖片處理方法,或者自己封裝圖片組件。
比如在該點餐頁面進入時需要獲取定位,然后根據定位獲取最近的門店,前面兩個接口都需要請求(具體可以根據業務需求),而最后如果獲取到的距離最近的門店跟上次一樣,則不需要重新獲取店鋪詳情和商品數據。
還是該點餐頁面流程,像上文說過的,進入頁面時需要獲取定位接口,等定位接口返回結果了再拿定位取值去獲取距離最近的店鋪,最后才是請求店鋪和商品數據。
這三個接口是串行的。此時如果我們每個接口都彈出一個loading提示,就會出現loading顯示一會兒,消失,又顯示一會兒,又消失……這樣的現象,這樣的體驗是不太好的。
建議可以通過封裝請求,并且在請求里統一處理loading,來合并短時間內多次發起請求的多個loading。
eg:
let showLoadingTimer = null;let showRequestLoading = false; // 標記是否正在顯示loading/** * 封裝request * @param {*} {showLoading:是否需要顯示loading, options:request參數,如url,data等} */function request({showLoading = true, ...options}) { // 顯示request loading handleShowLoading(showLoading) wx.request({ ... complete() { // 關閉request loading handleShowLoading(false) } }) }/** * 封裝request loading * 短時間內如果調用多次showLoading,會合并在一起顯示,而不是每個都閃現一下 * @param showLoading */function handleShowLoading(showLoading) { if (showLoading) { // 顯示loading clearTimeout(showLoadingTimer); if (!showRequestLoading) { showRequestLoading = true; wx.showNavigationBarLoading(); wx.showLoading({ title: "加載中", mask: true }) } } else { // 200ms后關閉loading showLoadingTimer = setTimeout(() => { showRequestLoading = false; wx.hideNavigationBarLoading(); wx.hideLoading() }, 200) } }復制代碼
比如這個點餐頁每次 onShow
都會調用定位接口和獲取最近門店接口,但是不顯示loading,用戶就沒有感知,體驗比較好。
需要關注接口的粒度控制。 因為有時候合并接口,前端可以減少一次請求,體驗更好;但有時候如果接口的數據太多,響應太慢,就可以考慮是否某部分數據可以后置獲取,讓主要的頁面內容先渲染出來,根據這個設計來拆分接口。
比如項目中的點餐頁面,原來購物車數據和商品規格彈窗顯示的詳情數據都是在獲取店鋪商品接口一次性返回的,而這個接口本來由于設計需要一次返回所有商品,就會造成數據量太大,而且后端需要查詢的表也更多。于是把獲取購物車,和商品詳情接口都拆分為單獨的接口,獲取店鋪商品接口的響應時間就減少了,頁面也能更快顯示出來。
以上是“小程序性能優化示例”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。