支付寶小程序框架 性能優(yōu)化建議

2020-09-18 10:34 更新

運行原理

與傳統(tǒng)的 H5 應(yīng)用不同,小程序運行架構(gòu)分為 webview 和 worker 兩個部分。webview 負(fù)責(zé)渲染,worker 則負(fù)責(zé)存儲數(shù)據(jù)和執(zhí)行業(yè)務(wù)邏輯。

  1. webview 和 worker 之間的通信是異步的。這意味著當(dāng)我們調(diào)用 setData 時,我們的數(shù)據(jù)并不會立即渲染,而是需要從 worker 異步傳輸?shù)?webview。
  2. 數(shù)據(jù)傳輸時需要序列化為字符串,然后通過 evaluateJavascript 方式傳輸,數(shù)據(jù)大小會影響性能。

優(yōu)化首屏

首屏有多種定義,這里的首屏是指業(yè)務(wù)角度第一次有意義的渲染。比如: 對于列表頁,首屏就是列表第一次渲染出的內(nèi)容。

控制小程序資源包大小

當(dāng)用戶訪問一個小程序時,支付寶客戶端會首先從 CDN 下載小程序資源包,所以資源包的大小會影響小程序啟動性能。

優(yōu)化建議

  • 及時刪除無用圖片資源,因為所有圖片資源都會默認(rèn)打包進(jìn)去
  • 控制圖片大小,避免使用大圖,大圖建議從 CDN 渠道上傳
  • 及時清理無用代碼

將數(shù)據(jù)請求提前至 onLoad

  • 小程序運行時,先觸發(fā)頁面的 onLoad 生命周期函數(shù),再將頁面初始數(shù)據(jù)(Page data)從 worker 傳遞到 webview 進(jìn)行一次初始渲染
  • 頁面初始渲染完成,從 webview 發(fā)出通知到 worker,觸發(fā) onReady 生命周期函數(shù)。

部分小程序會在 onReady 中發(fā)出請求,導(dǎo)致首屏渲染延緩。

優(yōu)化建議 將數(shù)據(jù)請求提前到 onLoad 中。

控制首屏一次性渲染節(jié)點數(shù)量

業(yè)務(wù)請求返回后,通常會調(diào)用 setData 觸發(fā)頁面重新渲染。執(zhí)行過程如下:

  1. 數(shù)據(jù)從 worker 傳遞到 webview
  2. webview 上根據(jù)傳過來的數(shù)據(jù)構(gòu)造虛擬 DOM,并與之前做差異比較(從根節(jié)點開始),然后渲染。

由于 worker 與 webview 通信時,數(shù)據(jù)需要序列化,然后到了 webview 需要執(zhí)行 evaluateJavascript,因此如果一次性傳輸數(shù)據(jù)太大,會影響首屏渲染性能。

另外,如果 webview 上構(gòu)造節(jié)點過多,層級嵌套太深,例如有的小程序列表頁面一次性渲染超過 100 個列表項,每個列表項又有嵌套內(nèi)容,而實際上整個屏幕可能只是顯示不到 10 個, 會導(dǎo)致差異比較時間較長,同時由于是首屏渲染,會一次性構(gòu)造很多 DOM,影響首屏渲染性能。

優(yōu)化建議

  • setData 數(shù)據(jù)量不宜過大,避免一次性傳遞過長的列表。
  • 首屏請勿一次性構(gòu)造太多節(jié)點,服務(wù)端可能一次請求傳遞大量數(shù)據(jù),請勿一次性 setData,可先 setData 一部分?jǐn)?shù)據(jù),然后等待一段時間(比如 400ms,具體需要業(yè)務(wù)調(diào)節(jié))再調(diào)用 $spliceData 將其他數(shù)據(jù)傳輸過去。

優(yōu)化 setData 邏輯

任何頁面變化都會觸發(fā) setData,同一時間可能會有多個 setData 觸發(fā)頁面進(jìn)行重新渲染。如下四個接口都會觸發(fā) webview 頁面重新渲染。

  • Page.prototype.setData: 觸發(fā)整個頁面做差異比較
  • Page.prototype.$spliceData: 針對長列表做優(yōu)化,避免每次傳遞整個列表,觸發(fā)整個頁面做差異比較
  • Component.prototype.setData: 只會從對應(yīng)組件節(jié)點開始做差異比較
  • Component.prototype.$spliceData: 針對長列表做優(yōu)化,避免每次傳遞整個列表,只會從對應(yīng)組件節(jié)點開始做差異比較

優(yōu)化建議

  • 避免頻繁觸發(fā) setData 或者 $spliceData,不管是頁面級別還是組件級別。在我們分析的案例中,有些頁面有倒計時邏輯,但是有的倒計時過于頻繁觸發(fā)(ms 級別的觸發(fā))。
  • 需要頻繁觸發(fā)重新渲染時,避免使用頁面級別的 setData 和 $spliceData, 將這一塊封裝成自定義組件,然后使用組件級別的 setData 或 $spliceData 觸發(fā)組件重新渲染。
  • 長列表數(shù)據(jù)觸發(fā)渲染時,使用 $spliceData 多次追加數(shù)據(jù),而不用傳遞整個列表。
  • 復(fù)雜頁面建議封裝成自定義組件,減少頁面級別的 setData

優(yōu)化案例

推薦指定路徑設(shè)置數(shù)據(jù):

this.setData({
  'array[0]': 1,
  'obj.x':2,
});

不推薦如下用法(雖然拷貝了this.data, 仍然直接更改了其屬性):

const array = this.data.array.concat();
array[0] = 1;
const obj={...this.data.obj};
obj.x=2;
this.setData({array,obj});

更不推薦直接更改 this.data(違反不可變數(shù)據(jù)原則):

this.data.array[0]=1;
this.data.obj.x=2;
this.setData(this.data)

長列表使用 $spliceData:

this.$spliceData({ 'a.b': [1, 0, 5, 6] })

注意

有時業(yè)務(wù)邏輯封裝到了組件中,當(dāng)組件 UI 需要重新渲染時,只需在組件內(nèi)部調(diào)用 setData。但有時需要從頁面觸發(fā)組件重新渲染,比如在頁面上監(jiān)聽了 onPageScroll 事件,當(dāng)事件觸發(fā)時,需要通知對應(yīng)組件重新渲染,此時的處理措施如下所示:

// pages/index/index.js
Page({
    onPageScroll(e) {
        if (this.xxcomponent) {
            this.xxcomponent.setData({
                scrollTop: e.scrollTop
            })
        }
    }
})
// components/index/index.js
Component({
    didMount(){
        this.$page.xxcomponent = this;
    }
})

可在組件的 didMount 中將組件掛載到對應(yīng)的頁面上,即可在頁面中調(diào)用組件級別的 setData 只觸發(fā)組件重新渲染。

使用 key 參數(shù)

在 for 中使用 key 來提高性能。 注意 key 不能設(shè)置在 block 上。

示例代碼:

<view a:for="{{array}}" key="{{item.id}}"></view>
<block a:for="{{array}}"><view key="{{item.id}}"></view></block>
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號