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

歷史回顧:

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

image

小程序常見(jiàn)性能問(wèn)題

  • 這個(gè)小程序?yàn)槭裁催@么慢飒货?
  • 這個(gè)小程序?yàn)槭裁椿粍?dòng)了,卡住了峭竣?
  • 小程序在切頁(yè)面的時(shí)候?yàn)槭裁磿?huì)有延遲塘辅?
  • 為什么點(diǎn)擊了沒(méi)有反應(yīng),是不是掛掉啦皆撩?

這些問(wèn)題的場(chǎng)景都反映了小程序的性能問(wèn)題扣墩,直接影響到用戶(hù)體驗(yàn)。

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

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

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

啟動(dòng)性能優(yōu)化

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

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

開(kāi)發(fā)者可以在第2滥比,3去優(yōu)化小程序的啟動(dòng)性能亚脆。

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

小程序在首次打開(kāi)時(shí),會(huì)去下載并執(zhí)行代碼包盲泛,隨著代碼包大小的上升濒持,耗時(shí)也會(huì)相應(yīng)增加∷鹿觯可以采取以下方案:

分包

image

使用分包

對(duì)開(kāi)發(fā)者而言柑营,能使小程序有更小的代碼體積,承載更多的功能與服務(wù)村视;而對(duì)用戶(hù)而言官套,可以更快地打開(kāi)小程序,同時(shí)在不影響啟動(dòng)速度前提下使用更多功能。

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

實(shí)踐

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

image

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

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

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

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

分包之后:

image

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

分包預(yù)下載

image

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

實(shí)踐

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

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

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

獨(dú)立分包

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

image

建議開(kāi)發(fā)者將部分對(duì)啟動(dòng)性能要求很高的頁(yè)面放到特殊的獨(dú)立分包中石挂。

實(shí)踐

項(xiàng)目中沒(méi)有適合的場(chǎng)景博助,尚未實(shí)踐。

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

image

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

大部分小程序在渲染首頁(yè)時(shí)誊稚,需要依賴(lài)服務(wù)端的接口數(shù)據(jù)翔始,接口請(qǐng)求放到頁(yè)面的生命周期 onLoad 中,而不是 onReady里里伯。

實(shí)踐

監(jiān)聽(tīng)到頁(yè)面加載城瞎,就校驗(yàn)登錄情況,請(qǐng)求頁(yè)面數(shù)據(jù)

onLoad: function (options) {
    app.checkAuth((error, token) => {
      if (error) {
        return
      }
      // 請(qǐng)求該頁(yè)面的數(shù)據(jù)
    })
  },

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

小程序提供了wx.setStorageSync等異步讀寫(xiě)本地緩存的能力疾瓮,數(shù)據(jù)存儲(chǔ)在本地脖镀,返回的會(huì)比網(wǎng)絡(luò)請(qǐng)求快。

實(shí)踐

登錄成功后將用戶(hù)的token狼电,以及用戶(hù)信息都可以緩存到本地蜒灰,記得退出登錄的時(shí)候清楚緩存,??肩碟。

/**
 * 設(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;
}

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

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

實(shí)踐

通過(guò)id請(qǐng)求詳情的情況咙崎,id在渲染層不需要优幸,就可以不把id,定義在data中:

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

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

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

4. 避免阻塞渲染

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

實(shí)踐

項(xiàng)目中沒(méi)有這樣使用座柱,有先見(jiàn)之明。??

渲染性能優(yōu)化

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

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

image

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

image

1. 頻繁的去 setData

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

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

實(shí)踐

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

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

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

image

實(shí)踐

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

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

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

image

實(shí)踐

A頁(yè)面上有個(gè)定時(shí)器,此時(shí)打開(kāi)了B頁(yè)面僻弹,A頁(yè)面的定時(shí)器還在運(yùn)行阿浓,繼續(xù)搶占B頁(yè)面的資源,B頁(yè)面卡頓了蹋绽,但是并不是B頁(yè)面的造成的性能問(wèn)題芭毙,這種問(wèn)題就不太好排查。希望大家都能做個(gè)有始有終的人卸耘,定時(shí)器不用了要清除退敦。下面demo,定時(shí)器在 onHide 時(shí)要清除掉鹊奖。切記切記??

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

/**
   * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面隱藏
 */
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)
  },

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

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

實(shí)踐

項(xiàng)目中展示沒(méi)用使用該事件忠聚。

3. 使用自定義組件

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

image

實(shí)踐

我們項(xiàng)目小程序打卡功能网梢,需要展示當(dāng)前到時(shí)間:時(shí)分秒,此處用定時(shí)器實(shí)現(xiàn)的赂毯,需要頻繁的更新setData战虏,此處就適合將該定時(shí)器提取為組件拣宰,讓其在組件內(nèi)部數(shù)據(jù)更新贮匕,不影響頁(yè)面的其它部分冈爹。

總結(jié)

image

小程序啟動(dòng)加載性能:

  • 控制代碼包的大小
  • 分包加載
  • 首屏體驗(yàn)

小程序渲染性能:

  • 避免不當(dāng)?shù)氖褂胹etData
  • 合理利用事件通信
  • 避免不當(dāng)?shù)氖褂胦nPageScroll
  • 優(yōu)化視圖節(jié)點(diǎn)
  • 使用自定義組件

注:文中PPT來(lái)自《2018微信公開(kāi)課第七季上海站·小程序?qū)?chǎng)》

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奋构,隨后出現(xiàn)的幾起案子手趣,更是在濱河造成了極大的恐慌晌该,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绿渣,死亡現(xiàn)場(chǎng)離奇詭異朝群,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)中符,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)姜胖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人淀散,你說(shuō)我怎么就攤上這事右莱。” “怎么了档插?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵隧出,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我阀捅,道長(zhǎng)胀瞪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任饲鄙,我火速辦了婚禮凄诞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忍级。我一直安慰自己帆谍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布轴咱。 她就那樣靜靜地躺著汛蝙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朴肺。 梳的紋絲不亂的頭發(fā)上窖剑,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音戈稿,去河邊找鬼西土。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鞍盗,可吹牛的內(nèi)容都是我干的需了。 我是一名探鬼主播跳昼,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼肋乍!你這毒婦竟也來(lái)了鹅颊?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤墓造,失蹤者是張志新(化名)和其女友劉穎挪略,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體滔岳,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年挽牢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谱煤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡禽拔,死狀恐怖刘离,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睹栖,我是刑警寧澤硫惕,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站野来,受9級(jí)特大地震影響恼除,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜曼氛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一豁辉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧舀患,春花似錦徽级、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至低匙,卻和暖如春旷痕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背顽冶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工苦蒿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渗稍。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓佩迟,卻偏偏與公主長(zhǎng)得像团滥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子报强,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359