微信小程序原生性能優化實踐

歷史回顧:javascript

本文主要是根據微信小程序官方優化建議和《2018微信公開課第七季上海站·小程序專場》的性能優化方案,針對性咱們的小程序項目進行性能優化實踐,將過程記錄下來,方便之後查看,同時也但願能幫助到其餘小夥伴,作好性能優化。畢竟一切性能優化都是爲了更好的用戶體驗。html

加綠色小旗子的優化方案是實踐過的java

小程序常見性能問題

  • 這個小程序爲何這麼慢?
  • 這個小程序爲何滑不動了,卡住了?
  • 小程序在切頁面的時候爲何會有延遲?
  • 爲何點擊了沒有反應,是否是掛掉啦?

這些問題的場景都反映了小程序的性能問題,直接影響到用戶體驗。webpack

小程序如何進行性能優化?

官方建議從這兩方面進行優化:web

  • 啓動性能優化
  • 渲染性能優化

啓動性能優化

小程序在整個啓動流程中,通常須要完成幾項工做:算法

  • 1.準備運行環境(微信本身處理的)
  • 2.下載,注入並執行對應小程序代碼包
  • 3.渲染小程序首頁

開發者能夠在第2,3去優化小程序的啓動性能。npm

1.代碼包大小優化

小程序在首次打開時,會去下載並執行代碼包,隨着代碼包大小的上升,耗時也會相應增長。能夠採起如下方案:json

分包小程序

使用分包

對開發者而言,能使小程序有更小的代碼體積,承載更多的功能與服務;而對用戶而言,能夠更快地打開小程序,同時在不影響啓動速度前提下使用更多功能。微信小程序

建議開發者按照功能的劃分,拆分紅幾個分包,當須要用到某個功能時,才加載這個功能對應的分包。

實踐

咱們的一個小程序在兩年多前開始開發的,在設計之初,咱們沒有考慮到這一點,當時也沒有小程序分包的功能。好吧,咱們仍是迎來了這個問題。

微信小程序在開發文檔中明確指出,小程序的全部包大小必須限制在2M之內,超過大小,就算在開發者工具中都不能正常預覽,更不能上傳發版。解決問題的方法:

將靜態資源圖片壓縮,由於小程序的壓縮算法對圖片的壓縮微乎其微,因而乎,筆者對圖片進行一輪壓縮,而且將重複使用的圖片,進行了公共提取,雖然官方推薦使用網絡圖片,可是還須要去維護靜態資源,嫌麻煩,就放棄了。

將項目中的棄用的頁面,以及不用的三方,進行了一波清除。

不少項目如今都是經過webpack打包成不一樣的分包,資源懶加載的形式來優化,小程序也提供了這個功能:分包,筆者按照按照功能劃分的原則,將同一個功能下的頁面和邏輯放置於同一個目錄下,成爲一個分包。

分包以後:

注意:1. 自定義第三方組件,須要放在主包內,miniprogram_npm文件會直接打到主包裏;2. 小程序的tab切換頁,必須放在主包裏。

分包預下載

分包預下載是爲了解決首次進入分包頁面時的延遲問題而設計的。若是可以在用戶進入分包頁面以前就預先將分包下載完畢,那麼進入分包頁面的延遲就可以儘量下降。

實踐

用戶進行了某個操做,再去下載分包,延遲操做致使用戶體驗不好,因而乎筆者對上面的分包設置分包預下載。在 app.json 文件中配置:

"preloadRule": {
    "pages/work/index": {
      "network": "all",
      "packages": [
        "package-work",
        "package-field-statistics"
      ]
    },
    "pages/appeal/index": {
      "network": "all",
      "packages": [
        "package-appeal"
      ]
    }
},
複製代碼

這裏建議不要一次性把全部分包預下載,這樣的操做一樣迴帶來性能問題。

獨立分包

小程序中的某些場景(如廣告頁、活動頁、支付頁等),一般功能不是很複雜且相對獨立,對啓動性能有很高的要求。使用獨立分包,能夠獨立於主包和其餘分包運行。從獨立分包中頁面進入小程序時,不須要下載主包。

建議開發者將部分對啓動性能要求很高的頁面放到特殊的獨立分包中。

實踐

項目中沒有適合的場景,還沒有實踐。

2.首屏渲染優化

1. 提早首屏數據請求

大部分小程序在渲染首頁時,須要依賴服務端的接口數據,接口請求放到頁面的生命週期 onLoad 中,而不是 onReady裏。

實踐

監聽到頁面加載,就校驗登陸狀況,請求頁面數據

onLoad: function (options) {
    app.checkAuth((error, token) => {
      if (error) {
        return
      }
      // 請求該頁面的數據
    })
  },
複製代碼

2. 緩存請求數據

小程序提供了wx.setStorageSync等異步讀寫本地緩存的能力,數據存儲在本地,返回的會比網絡請求快。

實踐

登陸成功後將用戶的token,以及用戶信息均可以緩存到本地,記得退出登陸的時候清楚緩存,😂。

/** * 設置本地 token 緩存 * @param {Object} session 服務器返回的數據 * @param {String} session.access_token 存取token * @param {String} session.refresh_token 刷新token * @param {String} session.expires_in 有效期限,以秒爲單位 */
export function set(session) {
  const localSession = Object.assign({}, session, {
    expires_timestamp: getExpireTimestamp(session.expires_in)
  });
  wx.setStorageSync(SESSION_KEY, localSession);

  _token = session.access_token;
}

export function clear() {
  wx.removeStorageSync(SESSION_KEY);
  clearTimeout(refresh_timer);

  _token = null;
}
複製代碼

3. 精簡首屏數據

推薦開發者延遲請求非關鍵渲染數據,縮短網絡請求時延,與視圖層渲染無關的數據儘可能不要放在 data 中,以避免傳輸垃圾數據,加快首屏渲染完成時間。

實踐

經過id請求詳情的狀況,id在渲染層不須要,就能夠不把id,定義在data中:

// 原來代碼
data: {
    id: ‘’,
    // ….
},
onLoad: function (options) {
    	this.setData({
		id: options.id
	})
	// ….
}

// 改寫後 不把id定義到data中
data: {
    // ….
},
app.checkAuth((error, token) => {
      const id = options.id === undefined ? '' : options.id;
      this.id = id 
})
複製代碼

接口返回的數據要作數據處理,不要直接都塞給data,減小冗餘數據的雙線程回傳。也是 精簡首屏數據優化的一部分。

4. 避免阻塞渲染

在小程序啓動流程中,會順序執行app.onLaunch, app.onShow, page.onLoad, page.onShow, page.onReady,因此,儘可能避免在這些生命週期中使用Sync結尾的同步API,如 wx.setStorageSync,wx.getSystemInfoSync 等。

實踐

項目中沒有這樣使用,有先見之明。😄

渲染性能優化

小程序的視圖層目前使用 WebView 做爲渲染載體,而邏輯層是由獨立的 JavascriptCore 做爲運行環境。在架構上,WebView 和 JavascriptCore 都是獨立的模塊,並不具有數據直接共享的通道。當前,視圖層和邏輯層的數據傳輸,實際上經過兩邊提供的 evaluateJavascript 所實現。即用戶傳輸的數據,須要將其轉換爲字符串形式傳遞,同時把轉換後的數據內容拼接成一份 JS 腳本,再經過執行 JS 腳本的形式傳遞到兩邊獨立環境。

而 evaluateJavascript 的執行會受不少方面的影響,數據到達視圖層並非實時的。

** 常見的 setData 操做錯誤 **

1. 頻繁的去 setData

致使了兩個後果:

  • Android 下用戶在滑動時會感受到卡頓,操做反饋延遲嚴重,由於 JS 線程一直在編譯執行渲染,未能及時將用戶操做事件傳遞到邏輯層,邏輯層亦沒法及時將操做處理結果及時傳遞到視圖層;
  • 渲染有出現延時,因爲 WebView 的 JS 線程一直處於忙碌狀態,邏輯層到頁面層的通訊耗時上升,視圖層收到的數據消息時距離發出時間已通過去了幾百毫秒,渲染的結果並不實時;

實踐

目前項目代碼仍是比較規範的,咱們並無把setData當成一個普通的對象去調用,曉得每次使用都須要兩個線程間通訊,WebView再去渲染的。哇,好棒。

2. 每次 setData 都傳遞大量新數據

由setData的底層實現可知,咱們的數據傳輸實際是一次 evaluateJavascript 腳本過程,當數據量過大時會增長腳本的編譯執行時間,佔用 WebView JS 線程。

實踐

目前每一個接口的數據量並大,數據的量級還沒達到影響腳步執行的程度,有須要的話再優化吧。

3. 後臺態頁面進行 setData

當頁面進入後臺態(用戶不可見),不該該繼續去進行setData,後臺態頁面的渲染用戶是沒法感覺的,另外後臺態頁面去setData也會搶佔前臺頁面的執行。

實踐

A頁面上有個定時器,此時打開了B頁面,A頁面的定時器還在運行,繼續搶佔B頁面的資源,B頁面卡頓了,可是並非B頁面的形成的性能問題,這種問題就不太好排查。但願你們都能作個善始善終的人,定時器不用了要清除。下面demo,定時器在 onHide 時要清除掉。切記切記👇

/** * 生命週期函數--監聽頁面顯示 */
onShow: function () {
    clearTimeout(getTodaytime)
    this.updateNowTime()
},

/** * 生命週期函數--監聽頁面隱藏 */
onHide: function () {
    // 取消定時器 防止小程序內存不足,崩潰
    clearTimeout(getTodaytime)
},
updateNowTime() {
    getTodaytime = setInterval(() => {
      const myDate = new Date(); 
      const hours = myDate.getHours())
      const minutes = myDate.getMinutes())
      const seconds = myDate.getSeconds())

      const newTime = hours + ':' + minutes + ':' + seconds;
      this.setData({
        newTime: newTime
      })
    }, 1000)
  },
複製代碼

2. 用戶事件使用不當

  • 過多的使用bindTap、bindCatch
  • 不當的使用onPageScroll

實踐

項目中展現沒用使用該事件。

3. 使用自定義組件

在須要頻繁更新的場景下,自定義組件的更新只在組件內部進行,不受頁面其餘部份內容複雜性的影響。

實踐

咱們項目小程序打卡功能,須要展現當前到時間:時分秒,此處用定時器實現的,須要頻繁的更新setData,此處就適合將該定時器提取爲組件,讓其在組件內部數據更新,不影響頁面的其它部分。

總結

小程序啓動加載性能:

  • 控制代碼包的大小
  • 分包加載
  • 首屏體驗

小程序渲染性能:

  • 避免不當的使用setData
  • 合理利用事件通訊
  • 避免不當的使用onPageScroll
  • 優化視圖節點
  • 使用自定義組件

注:文中PPT來自《2018微信公開課第七季上海站·小程序專場》

參考

相關文章
相關標籤/搜索