百度小程序性能優化


引子:  

一個指標引發的血案

        

    

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

    

    

性能優化

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

一 小程序運行時簡介

  在傳統的Web前端項目中,所有代碼全部運行在瀏覽器中。而小程序提供的運行環境有兩種,分為邏輯層和視圖層。假設現在開發者的小程序項目中有兩個頁面pages/index和pages/home,那么邏輯層代碼指的是app.jspages/index/index.js還有pages/home/home.js,視圖層代碼指的是pages/index/index.swanpages/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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM