小程序模板網(wǎng)

微信小程序性能優(yōu)化實(shí)踐

發(fā)布時(shí)間:2020-05-13 10:00 所屬欄目:小程序開發(fā)教程

本文主要是根據(jù)微信小程序官方優(yōu)化建議和《2018微信公開課第七季上海站·小程序?qū)觥返男阅軆?yōu)化方案,針對性我們的小程序項(xiàng)目進(jìn)行性能優(yōu)化實(shí)踐,將過程記錄下來,方便以后查看,同時(shí)也希望能幫助到其他小伙伴,做好性能優(yōu)化。畢竟一切性能優(yōu)化都是為了更好的用戶體驗(yàn)。

小程序常見性能問題

  • 這個(gè)小程序?yàn)槭裁催@么慢?
  • 這個(gè)小程序?yàn)槭裁椿粍恿耍ㄗ×耍?/li>
  • 小程序在切頁面的時(shí)候?yàn)槭裁磿醒舆t?
  • 為什么點(diǎn)擊了沒有反應(yīng),是不是掛掉啦?

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

小程序如何進(jìn)行性能優(yōu)化?

官方建議從這兩方面進(jìn)行優(yōu)化:

  • 啟動性能優(yōu)化
  • 渲染性能優(yōu)化

啟動性能優(yōu)化

小程序在整個(gè)啟動流程中,一般需要完成幾項(xiàng)工作:

  • 1.準(zhǔn)備運(yùn)行環(huán)境(微信自己處理的)
  • 2.下載,注入并執(zhí)行對應(yīng)小程序代碼包
  • 3.渲染小程序首頁

開發(fā)者可以在第2,3去優(yōu)化小程序的啟動性能。

1.代碼包大小優(yōu)化

小程序在首次打開時(shí),會去下載并執(zhí)行代碼包,隨著代碼包大小的上升,耗時(shí)也會相應(yīng)增加??梢圆扇∫韵路桨福?/p>

分包

使用分包

對開發(fā)者而言,能使小程序有更大的代碼體積,承載更多的功能與服務(wù);而對用戶而言,可以更快地打開小程序,同時(shí)在不影響啟動速度前提下使用更多功能。

建議開發(fā)者按照功能的劃分,拆分成幾個(gè)分包,當(dāng)需要用到某個(gè)功能時(shí),才加載這個(gè)功能對應(yīng)的分包。

使用分包實(shí)踐

我們的一個(gè)小程序在兩年多前開始開發(fā)的,在設(shè)計(jì)之初,我們沒有考慮到這一點(diǎn),當(dāng)時(shí)也沒有小程序分包的功能。好吧,我們還是迎來了這個(gè)問題。

微信小程序在開發(fā)文檔中明確指出,小程序的所有包大小必須限制在2M以內(nèi),超過大小,就算在開發(fā)者工具中都不能正常預(yù)覽,更不能上傳發(fā)版。解決問題的方法:

將靜態(tài)資源圖片壓縮,因?yàn)樾〕绦虻膲嚎s算法對圖片的壓縮微乎其微,于是互,筆者對圖片進(jìn)行一輪壓縮,并且將重復(fù)使用的圖片,進(jìn)行了公共提取,雖然官方推薦使用網(wǎng)絡(luò)圖片,但是還需要去維護(hù)靜態(tài)資源,嫌麻煩,就放棄。

將項(xiàng)目中的棄用的頁面,以及不用的三方,進(jìn)行了一波清除。

很多項(xiàng)目現(xiàn)在都是通過webpack打包成不通的分包,資源懶加載的形式來優(yōu)化,小程序也提供了這個(gè)功能:分包,筆者按照按照功能劃分的原則,將同一個(gè)功能下的頁面和邏輯放置于同一個(gè)目錄下,成為一個(gè)分包。

分包之后:

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

分包預(yù)下載

分包預(yù)下載是為了解決首次進(jìn)入分包頁面時(shí)的延遲問題而設(shè)計(jì)的。如果能夠在用戶進(jìn)入分包頁面之前就預(yù)先將分包下載完畢,那么進(jìn)入分包頁面的延遲就能夠盡可能降低。

實(shí)踐

用戶進(jìn)行了某個(gè)操作,再去下載分包,延遲操作用戶體驗(yàn)很差,于是乎筆者對上面的分包設(shè)置分包預(yù)下載。在 app.json 文件中配置:

"preloadRule": {
    "pages/work/index": {
      "network": "all",
      "packages": [
        "package-work",
        "package-field-statistics"
      ]
    },
    "pages/appeal/index": {
      "network": "all",
      "packages": [
        "package-appeal"
      ]
    }
},
復(fù)制代碼

這里建議不要一次性把所有分包預(yù)下載,這樣的操作同樣回帶來性能問題。

獨(dú)立分包

小程序中的某些場景(如廣告頁、活動頁、支付頁等),通常功能不是很復(fù)雜且相對獨(dú)立,對啟動性能有很高的要求。使用獨(dú)立分包,可以獨(dú)立于主包和其他分包運(yùn)行。從獨(dú)立分包中頁面進(jìn)入小程序時(shí),不需要下載主包。

建議開發(fā)者將部分對啟動性能要求很高的頁面放到特殊的獨(dú)立分包中。

實(shí)踐

項(xiàng)目中沒有適合的場景,尚未實(shí)踐。

2.首屏渲染優(yōu)化

1. 提前首屏數(shù)據(jù)請求

大部分小程序在渲染首頁時(shí),需要依賴服務(wù)端的接口數(shù)據(jù),接口請求放到頁面的生命周期 onLoad 中,而不是 onReady 里。 `:

實(shí)踐

監(jiān)聽到頁面加載,就校驗(yàn)登錄情況,請求頁面數(shù)據(jù)

onLoad: function (options) {
    app.checkAuth((error, token) => {
      if (error) {
        return
      }
      // 請求該頁面的數(shù)據(jù)
    })
  },
復(fù)制代碼

2. 緩存請求數(shù)據(jù)

小程序提供了wx.setStorageSync等異步讀寫本地緩存的能力,數(shù)據(jù)存儲在本地,返回的會比網(wǎng)絡(luò)請求快。

實(shí)踐

登錄成功后將用戶的token,以及用戶信息都可以緩存到本地,記得退出登錄的時(shí)候清楚緩存,:joy:。

/**
 * 設(shè)置本地 token 緩存
 * @param {Object} session 服務(wù)器返回的數(shù)據(jù)
 * @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;
}
復(fù)制代碼

3. 精簡首屏數(shù)據(jù)

推薦開發(fā)者延遲請求非關(guān)鍵渲染數(shù)據(jù),縮短網(wǎng)絡(luò)請求時(shí)延,與視圖層渲染無關(guān)的數(shù)據(jù)盡量不要放在 data 中,以免傳輸垃圾數(shù)據(jù),加快首屏渲染完成時(shí)間。

實(shí)踐

通過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 
})
復(fù)制代碼

接口返回的數(shù)據(jù)要做數(shù)據(jù)處理,不要直接都塞給data,減少冗余數(shù)據(jù)的雙線程回傳。也是 精簡首屏數(shù)據(jù)優(yōu)化的一部分。

4. 避免阻塞渲染

在小程序啟動流程中,會順序執(zhí)行app.onLaunch, app.onShow, page.onLoad, page.onShow, page.onReady,所以,盡量避免在這些生命周期中使用Sync結(jié)尾的同步API,如 wx.setStorageSync,wx.getSystemInfoSync 等。

實(shí)踐

項(xiàng)目中沒有這樣使用,有先見之明。:smile:

渲染性能優(yōu)化

小程序的視圖層目前使用 WebView 作為渲染載體,而邏輯層是由獨(dú)立的 JavascriptCore 作為運(yùn)行環(huán)境。在架構(gòu)上,WebView 和 JavascriptCore 都是獨(dú)立的模塊,并不具備數(shù)據(jù)直接共享的通道。當(dāng)前,視圖層和邏輯層的數(shù)據(jù)傳輸,實(shí)際上通過兩邊提供的 evaluateJavascript 所實(shí)現(xiàn)。即用戶傳輸?shù)臄?shù)據(jù),需要將其轉(zhuǎn)換為字符串形式傳遞,同時(shí)把轉(zhuǎn)換后的數(shù)據(jù)內(nèi)容拼接成一份 JS 腳本,再通過執(zhí)行 JS 腳本的形式傳遞到兩邊獨(dú)立環(huán)境。

而 evaluateJavascript 的執(zhí)行會受很多方面的影響,數(shù)據(jù)到達(dá)視圖層并不是實(shí)時(shí)的。

** 常見的 setData 操作錯(cuò)誤 **

1. 頻繁的去 setData

導(dǎo)致了兩個(gè)后果:

  • Android 下用戶在滑動時(shí)會感覺到卡頓,操作反饋延遲嚴(yán)重,因?yàn)?JS 線程一直在編譯執(zhí)行渲染,未能及時(shí)將用戶操作事件傳遞到邏輯層,邏輯層亦無法及時(shí)將操作處理結(jié)果及時(shí)傳遞到視圖層;
  • 渲染有出現(xiàn)延時(shí),由于 WebView 的 JS 線程一直處于忙碌狀態(tài),邏輯層到頁面層的通信耗時(shí)上升,視圖層收到的數(shù)據(jù)消息時(shí)距離發(fā)出時(shí)間已經(jīng)過去了幾百毫秒,渲染的結(jié)果并不實(shí)時(shí);

實(shí)踐

目前項(xiàng)目代碼還是比較規(guī)范的,我們并沒有把setData當(dāng)成一個(gè)普通的對象去調(diào)用,曉得每次使用都需要兩個(gè)線程間通信,WebView再去渲染的。哇,好棒。

2. 每次 setData 都傳遞大量新數(shù)據(jù)

由setData的底層實(shí)現(xiàn)可知,我們的數(shù)據(jù)傳輸實(shí)際是一次 evaluateJavascript 腳本過程,當(dāng)數(shù)據(jù)量過大時(shí)會增加腳本的編譯執(zhí)行時(shí)間,占用 WebView JS 線程。

實(shí)踐

目前每個(gè)接口的數(shù)據(jù)量并大,數(shù)據(jù)的量級還沒達(dá)到影響腳步執(zhí)行的程度,有需要的話再優(yōu)化吧。

3. 后臺態(tài)頁面進(jìn)行 setData

當(dāng)頁面進(jìn)入后臺態(tài)(用戶不可見),不應(yīng)該繼續(xù)去進(jìn)行setData,后臺態(tài)頁面的渲染用戶是無法感受的,另外后臺態(tài)頁面去setData也會搶占前臺頁面的執(zhí)行。

實(shí)踐

A頁面上有個(gè)定時(shí)器,此時(shí)打開了B頁面,A頁面的定時(shí)器還在運(yùn)行,繼續(xù)搶占B頁面的資源,B頁面卡頓了,但是并不是B頁面的造成的性能問題,這種問題就不太好排查。希望大家都能做個(gè)有始有終的人,定時(shí)器不用了要清除。下面demo,定時(shí)器在 onHide 時(shí)要清除掉。切記切記:point_down:

/**
   * 生命周期函數(shù)--監(jiān)聽頁面顯示
   */
onShow: function () {
    clearTimeout(getTodaytime)
    this.updateNowTime()
},

/**
   * 生命周期函數(shù)--監(jiān)聽頁面隱藏
 */
onHide: function () {
    // 取消定時(shí)器 防止小程序內(nèi)存不足,崩潰
    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)
  },
復(fù)制代碼

2. 用戶事件使用不當(dāng)

  • 過多的使用bindTap、bindCatch
  • 不當(dāng)?shù)氖褂胦nPageScroll

實(shí)踐

項(xiàng)目中展示沒用使用該事件。

3. 使用自定義組件

在需要頻繁更新的場景下,自定義組件的更新只在組件內(nèi)部進(jìn)行,不受頁面其他部分內(nèi)容復(fù)雜性的影響。


易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉庫:starfork
本文地址:http://m.u-renovate.com/wxmini/doc/course/25130.html 復(fù)制鏈接 如需定制請聯(lián)系易優(yōu)客服咨詢:800182392 點(diǎn)擊咨詢
QQ在線咨詢
AI智能客服 ×