天天看點

百度小程式性能優化

引子:  

一個名額引發的血案

       https://www.cnblogs.com/Sherlock09/p/11726885.html

性能優化

  進入移動網際網路時代,傳統Web開發技術(HTML,CSS,JavaScript)風光不再,用戶端技術(iOS以及Android)依靠良好的體驗重新崛起。但是用戶端技術的開發效率始終無法與Web技術抗衡,同時會受到諸多平台層面的限制。在這一大背景下,小程式獨特的架構誕生了,它将Web前端技術與傳統的用戶端技術結合在一起,其目的是在開發效率上超過傳統的用戶端技術,在使用體驗上超越傳統的Web前端技術。由于小程式的架構差別于傳統的Web前端技術,開發者在開發過程可能會遇到一些性能上的問題。本文旨在介紹百度小程式一些實作原理和優化手段,幫助開發者優化自己的小程式。

一 小程式運作時簡介

  在傳統的Web前端項目中,所有代碼全部運作在浏覽器中。而小程式提供的運作環境有兩種,分為邏輯層和視圖層。假設現在開發者的小程式項目中有兩個頁面

pages/index和pages/home

,那麼邏輯層代碼指的是

app.js

pages/index/index.js

還有

pages/home/home.js

,視圖層代碼指的是

pages/index/index.swan

pages/home/home.swan

。在小程式中想要改變視圖需要邏輯層與視圖層之間進行通信,這部分通信是需要用戶端參與,會消耗一定的系統資源。

  存在問題:setData 調用随意、頻繁,有很多不必要的資料沒有必要存放在data中

  setData操作的優化

  setData方法是開發者通過邏輯層向視圖層發送資料的方法。每一次 setData 的調用,都會觸發一次通信,而每一次的通信都會消耗一定的系統資源,是以,開發者在使用 setData 需要注意以下幾點:

  1. 不要過于頻繁調用setData,應考慮将多次setData合并成一次setData調用。
  2. 不在視圖層使用的資料不要通過setData傳輸。
  3. 不在頁面不可見之後使用setData.
  4. 不建議在更新資料結構當中的某一子項的時候将整個資料結構放到setData方法中,可以通過優化setData的key值來實作。
    • 錯誤寫法:
      let person = this.getData(\'person\');
      person.age = 30;
      this.setData(\'person\',person);      
    • 正确寫法:
      this.setData(\'person.age\', 30);      
    • 在更新清單中某一項内部的值時,推薦的用法為:
      this.setData(\'list[0].person.name\', \'Harry\');      
  5. 在處理無限滾動頁面加載的時候,我們發現很多開發者将新的一頁上的資料添加整體的資料裡面再調用setData。這樣做造成每次頁面加載傳輸的資料越來越大。
    • 未優化情況下的做法:
      let pages = this.data.pages.push(pageData)
      this.setData({
       list: pages
      });      
    • 優化後的做法:
      Page({
       data: {
       pages: [], // 使用一個二維數組來描述長清單
       currentPage: 0 // 目前頁面的頁碼
       },
       onReady() {
       // 如果需要更新一頁的資料,那麼直接更新二維數組中的項
       this.setData(`pages[${currentPage}]`, pageData);
       }
      });      
  6. 使用trackBy來優化清單更新時的渲染性能
    • 當使用下拉重新整理功能時,新的資料會被添加到目前清單的頭部,這種情況下,頁面中清單内所有的項都會被重新渲染一次。
      // 下拉重新整理更新方式舉例
      let list = list.unshift(newPage);
      this.setData({
       list
      });      
    • 如果使用trackBy,那麼原先的清單内的項位置會移動,新添加的項會被渲染。這樣可以省去一部分重新渲染帶來的消耗。
      // 使用trackBy舉例
      <scroll-view>
       <view s-for="item, index in list trackBy item.id">
       </view>
      </scroll-view>      

  優化:

  1. 由于代碼中的setData 會增加邏輯層與渲染層間的線程通信,是以要避免頻繁的調用setData,将相應的調用合并

  2. 對于頁面中不涉及渲染的變量,從data中拆分出來(待),scene這些公用的資料可以放在初始化的app.js中

百度小程式性能優化

3. 小程式版本和api 更新疊代快,廢棄掉原來舊的影響性能的api,例如getData這個api,在之前的版本都是可以用的,在新的版本中雖然也可以用,但是由于對性能有一定影響,是以

    遵循優化的原則,改掉這些對性能有影響的api

二 包體積優化

  減小包的體積可以減少包的下載下傳時間。根據已經上線的小程式包的統計分析結果,小程式官方将主包的體積控制在 1M 左右,包内的檔案個數限制在 200 以内。除了體積之外,小程式包内的檔案個數也直接影響到小程式包的解壓速度。是以,需要減少小程式包内非必須的圖檔、字型、音頻等資源的檔案個數。同時,邏輯層與視圖層的代碼都需要加載到 webview 執行個體當中去,減小這部分的體積也會加快小程式的啟動速度。

  存在問題:包體積過大,目前主包大小在1.44M

優化方案:

  1. 分包加載

  分包加載是智能小程式用來解決包體積過大而給出的一個技術解決方案(點選檢視分包加載相關文檔)。為了最大程度的減少主包的大小,提高小程式的加載速度,開發者使用分包加載政策時,建議将必須的和經常通路的頁面放入主包當中,例如将聲明在 app.json 當中的 tabBar 配置項下的頁面放入主包當中。另外,根據小程式的投放場景不同,開發者需要仔細思考自己的小程式中哪些頁面是經常被通路的。舉個例子,在Feed和搜尋分發的小程式非首頁頁面我們建議放到主包中,避免首次打開投放頁面處于分包内時需要先下載下傳主包再下載下傳分包而導緻的性能退化。

{
 "pages": [
 // 該配置項下經常要通路的頁面放入主包當中,其餘頁面放入子包當中
 "pages/index/index",
 "pages/detail/detail"
 ],
 "tabBar": {
 // 該配置項下面的頁面建議放入主包當中
 "list": [
 {
 "pagePath": "pages/index/index",
 "text": "首頁"
 },
 {
 "pagePath": "pages/logs/logs",
 "text": "日志"
 }
 ]
 }
}      

  2.圖檔的優化

  • 原則上除小程式 icon 以外的圖檔資源均應部署到 cdn 上,不建議把所有的圖檔都放在小程式包内,這樣會增大包的體積。影響小程式包的下載下傳速度與解壓速度。
  • 過大的圖檔在加載時會消耗更多的系統資源。是以建議開發者盡量不使用超過 300K 的圖檔資源。
  • 對小程式包内的圖檔選擇合适的格式進行存儲,不需要透明格式的圖檔,推薦采用 jpeg 格式來存儲代替 png 格式。
  • 适當的降低圖檔的品質,大多數場景我們并不需要 100% 的 JPEG 壓縮比例,此時我們可以修改 JPEG 的壓縮比例進而大幅減小 JPEG 圖檔的體積。例如:100% 的 JPEG 圖檔與 25% 壓縮比大小差為 90%,但是肉眼可見的感覺可能微乎其微。
  • 對小程式包内的圖檔進行适當的壓縮,對于 png 格式的圖檔,最常用的工具是tinypng。對于jpeg格式的圖檔,可以使用的工具是tinyjpg。也可以使用EXIF Purge或者其他圖檔編輯軟體來清除圖檔的exif資訊,減小圖檔的體積。
  • 去除小程式包内備援和無用的圖檔資源,例如:重複的圖檔,未引用或不需要的資源檔案等。
  • 安卓端支援webp的圖檔格式,webp圖檔格式在有損壓縮的情況下,肉眼不易察覺出壓縮前後的變化,但是體積卻得到很大的減小。需要注意的是,iOS平台下的小程式不支援webp格式,如果開發者要使用webp格式的圖檔的話,需要注意區分平台。
百度小程式性能優化

  3. 圖檔元件懶加載

  其他資源檔案的優化

  JSON描述檔案可以通過jsonminify工具對JSON檔案進行壓縮。

三 請求資料的優化

    絕大多數小程式都需要請求服務端來擷取渲染頁面的資料,對于請求資料的優化,總結起來就是一句話,關鍵的早請求,不關鍵的晚請求。

    涉及到關鍵資料的異步請求可以盡早的發出,不需要等待頁面的 onReady 生命周期之後才去發送請求。這樣可以讓頁面所需的資料盡可能早的處于 Ready 狀态。除了在現有的生命周期發送資料請求以外,我們還提供了prefetch機制(prefetch機制需要在app.json之中進行配置)能夠在小程式架構啟動階段就去請求資料,而不用等待頁面生命周期觸發。

    根據小程式被打開的場景,可以對異步請求進行優先級排序,不重要的請求放在頁面的 onReady 生命周期去請求。例如,貼吧小程式最經常被通路的頁面是文章内容頁,是以除了目前文章内容以外的資料請求都是非關鍵請求,可以将觸發的時機延後,保證文章内容盡可能早的被加載出來。

四 清理定時器

  當使用

swan.navigateTo

進行頁面跳轉的時候,舊頁面是沒有被銷毀的。舊頁面當中定義的定時器仍舊會運作。是以在頁面跳轉的時候,一定要記住清理沒有用的定時器:

Page({
 onReady() {
 this.timer = setInterval(() => {
 // do something
 }, 300);
 },
 onHide() {
 // 在頁面不在前台顯示的時候,清除無用的定時器
 this.timer && clearInterval(this.timer);
 }
})      

五 合理使用自定義元件

  自定義元件與模闆内的import與include功能都可以達到代碼複用的效果。需要注意的是,如果自定義元件内沒有邏輯層的功能的話,這時候使用自定義元件就是非必須的了。我們可以用下面的方式實作代碼的複用:

<import src="./person.swan" />
<view class="container">
 <!-- 使用import複用模闆代碼 -->
 <template is="person-card" data="{{person}}" />
</view>      
// Person相關函數
export function play() { /* do something*/ }
export function eat() { /* do something*/ }
export function sing() { /* do something*/ }      
// 複用person.js中的函數
import * as Person from \'./person\';
Page({
 onReady() {
 this.play();
 this.eat();
 this.sing();
 },
 onShow() {},
 onLoad() {},
 onHide() {},
 ...Person
})      

六 漸進式加載l

    提前加載頁面的骨架,可以減少使用者的白屏等待時長,百度智能小程式提供了漸進式加載機制,使用這一機制,可以給使用者帶來更好的使用者體驗。下面将介紹如何使用這一機制為開發者自己的小程式提供漸進式加載的能力。

    這裡插一句額外的,在其他webpack的項目中,也可以使用page-skeleton-webpack-plugin,這個插件來生成骨架屏,具體可參考

    https://github.com/ElemeFE/page-skeleton-webpack-plugin    

  • 第一步:在工程項目根目錄建立skeleton檔案夾(除了config.jso以外的檔案目錄可自定義名稱),目錄如下所示
    skeleton
    |--- page/
    | |--- index.tpl 骨架屏模闆代碼
    | |--- list.tpl 骨架屏模闆代碼
    |___ config.json page和骨架屏的映射關系      
  • 第二步:使用标準HTML與CSS,編寫骨架屏模闆檔案,如index.tpl骨架屏代碼如下圖
    <style>
    .skeleton-list {
     background: gray;
    }
    </style>
    <div style="width:100%">
     <ul class="skeleton-list">
     <li></li>
     <li></li>
     <li></li>
     <li></li>
     </ul>
    </div>      
  • 第三步:配置config.json檔案,pages和骨架屏是多對一的映射關系,可配置多個頁面對應同一個骨架屏模闆
    {
     "pages/home/index": "skeleton/page/index",
     "pages/list/index": "skeleton/page/index"
    }      
說明
1. 需要目前最新的開發者工具rc版本與百度App 11.10及其以上版本
2. 骨架屏移除的時機由開發者自己掌控。開發者可以在Page内通過調用this.removeSkeleton()移除。           

七 白屏優化

     目前首頁白屏率

  排查異常

  小程式白屏資料出現異常上漲時,可以從以下三個方面着手排查分析:

  服務穩定性

  1. 小程式頁面資料請求是否正常:

    通過線上巡檢,發現有小程式存在自身服務不穩定的情況。例如小程式頁面資料請求傳回4XX,5XX錯誤等。

  2. HTTPS證書是否存在問題:

    排查HTTPS證書是否已過期,導緻小程式相關請求失敗,無法展示資料。

    有些小程式可能誤使用了自簽的HTTPS證書,由于無法被信任,使用者也無法強制信任,導緻頁面資料擷取失敗。

  業務邏輯

  有些小程式的頁面資料展示可能存在前置條件,例如需要登入、定位等。在條件不滿足時,可能存在相容處理問題。這裡給出常見的幾種case:

  1. 頁面打開時需要首先進行授權,擷取權限:

    授權失敗時需要有響應的相容邏輯或者給予明确提示。

  2. 頁面打開時需要登入才可展示内容:

    例如常見的購物類小程式,使用者未登入時需要有相應的提示,以及觸發登入的按鈕或者入口。

  3. 網絡連接配接失敗時,頁面相容性不足:

    這種情況最好是有對應的錯誤頁和重試入口,保證使用者可再操作,提供自主恢複的能力。

  4. 邏輯中存在自設校驗,校驗不通過:

    有些小程式是從微信小程式遷移而來,内部邏輯中可能存在自設的平台檢測校驗等,遷移時或者版本更新時沒有同步變更,導緻校驗不通過,進而導緻頁面異常。

  架構相容性

  小程式架構自身也在不斷更新,所支援的能力也在不斷更新和擴充。同樣,開發者也會對小程式自身也會進行版本更新。這裡就涉及到了相容性問題。小程式架構版本修複Bug記錄和版本相容性,請參考以下連接配接了解和主動規避:

  1. 文法支援性:運作環境
  2. 版本相容性:相容性說明
  3. 架構更新日志及修複問題說明:曆史更新日志

  優化性能和體驗

  已有啟動性能資料,平均資料和80分位資料較快不一定能保證白屏率就低,白屏case大機率發生在性能的長尾資料中。

從平台跟進的多個小程式白屏資料分析結果來看,小程式白屏率高的主要因素是頁面資料加載和渲染較慢。如果小程式上線後白屏資料就處于高位,或者版本更新後白屏資料上漲,可以通過以下方面進行分析和優化:

  1. 頁面結構:

    部分小程式的頁面内容重度依賴于伺服器的傳回,在服務端沒有資料傳回的時候,頁面沒有任何内容展示,這樣的情況在遇到網絡波動或者服務發生抖動的時候會造成白屏率的陡增。開發者可以在服務端資料傳回之前通過動畫,文案體驗上的優化來減小白屏率。

  2. 頁面資料加載方式:

    針對一次請求傳回的資料過多的情況,可以從兩個角度來優化:1 、非關鍵資料延遲請求,2、非關鍵資料延遲渲染

    非關鍵資料延遲請求:

    swan.request({
     url: \'https://www.baidu.com/keyData\',
     success: res => {
     this.setData({
     keyData: res
     });
     swan.request({
     url: \'https://www.baidu.com/nonKeyData\',
     success: res => {}
     });
     }
    })      
    非關鍵資料延遲渲染
    this.setData({keyData}, () => {
     this.setData({nonKeyData});
    });      
  3. 增加過渡态提示:

    頁面加載時,可以使用Loading元件等形式進行提示,給使用者一個提示,提升使用者體驗。

  4. 使用骨架屏:

    骨架屏形式類似下圖,可以很好的提升使用者使用小程式時的體驗。

  5. 預設态處理:

    例如在涉及到定位時,部分小程式會等待定位完成後才展示資料,可以增加預設态資料,定位完成後再更新頁面資料。

總結:

    上線四五天後,整體效果有了一定的提升,由于使用者收斂和老使用者更新版本的影響,最終的效果還需再觀察一段時間

    通過這次性能優化的調整,體會到了小程式這種運作在端内的代碼對于性能的苛刻要求,你的每一部分代碼的簡潔程度,代碼組織的調整,靜态資源大小、部署、加載的政策

都深深影響到小程式加載的整體名額。

    同時,日常生活中的代碼,也有同樣的問題

    1. 對于新的代碼,是否做到了合理的書寫?拆分?便于維護?

    2. 對于備援的代碼,實際上嚴重影響着代碼整體加載效率,是否有目的的清理和調整?

    3. 對于靜态資源,一些圖檔、字型,圖檔有沒有壓縮合并?備援的一張圖檔有可能比備援的一個jquery更大?

    4. 對于舊的api,拿vue2.0舉例,在v-for的時候,如果把key添加上,将大大提升Vnode diff的效率,這塊是否有意識的去優化

    5. 很多東西,實際上我們都知道應該去做,是否去主動的做一些,哪怕做一點,哪怕這次去一個  沒用 || 已經  改版了的圖檔、js、css(主要)

注:由于内部安全紅線,相關業務資訊已隐藏,轉載請著名出處(https://www.cnblogs.com/Sherlock09/p/11726885.html)