百度小程式性能優化
- 2019 年 10 月 23 日
- 筆記
引子:
一個指標引發的血案
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 需要注意以下幾點:
- 不要過於頻繁調用setData,應考慮將多次setData合併成一次setData調用。
- 不在視圖層使用的數據不要通過setData傳輸。
- 不在頁面不可見之後使用setData.
-
不建議在更新數據結構當中的某一子項的時候將整個數據結構放到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');
-
-
在處理無限滾動頁面載入的時候,我們發現很多開發者將新的一頁上的數據添加整體的數據裡面再調用setData。這樣做造成每次頁面載入傳輸的數據越來越大。
-
未優化情況下的做法:
let pages = this.data.pages.push(pageData)
this.setData({
list: pages
}); -
優化後的做法:
Page({
data: {
pages: [], // 使用一個二維數組來描述長列表
currentPage: 0 // 當前頁面的頁碼
},
onReady() {
// 如果需要更新一頁的數據,那麼直接更新二維數組中的項
this.setData(`pages[${currentPage}]`, pageData);
}
});
-
-
使用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和搜索分發的小程式非首頁頁面我們建議放到主包中,避免首次打開投放頁面處於分包內時需要先下載主包再下載分包而導致的性能退化。
{
|
2.圖片的優化
- 原則上除小程式 icon 以外的圖片資源均應部署到 cdn 上,不建議把所有的圖片都放在小程式包內,這樣會增大包的體積。影響小程式包的下載速度與解壓速度。
- 過大的圖片在載入時會消耗更多的系統資源。所以建議開發者盡量不使用超過 300K 的圖片資源。
- 對小程式包內的圖片選擇合適的格式進行存儲,不需要透明格式的圖片,推薦採用 jpeg 格式來存儲代替 png 格式。
- 適當的降低圖片的品質,大多數場景我們並不需要 100% 的 JPEG 壓縮比例,此時我們可以修改 JPEG 的壓縮比例從而大幅減小 JPEG 圖片的體積。例如:100% 的 JPEG 圖片與 25% 壓縮比大小差為 90%,但是肉眼可見的感知可能微乎其微。
- 對小程式包內的圖片進行適當的壓縮,對於 png 格式的圖片,最常用的工具是tinypng。對於jpeg格式的圖片,可以使用的工具是tinyjpg。也可以使用EXIF Purge或者其他圖片編輯軟體來清除圖片的exif資訊,減小圖片的體積。
- 去除小程式包內冗餘和無用的圖片資源,例如:重複的圖片,未引用或不需要的資源文件等。
- Android端支援webp的圖片格式,webp圖片格式在有損壓縮的情況下,肉眼不易察覺出壓縮前後的變化,但是體積卻得到很大的減小。需要注意的是,iOS平台下的小程式不支援webp格式,如果開發者要使用webp格式的圖片的話,需要注意區分平台。
3. 圖片組件懶載入
其他資源文件的優化
JSON描述文件可以通過jsonminify工具對JSON文件進行壓縮。
三 請求數據的優化
絕大多數小程式都需要請求服務端來獲取渲染頁面的數據,對於請求數據的優化,總結起來就是一句話,關鍵的早請求,不關鍵的晚請求。
涉及到關鍵數據的非同步請求可以儘早的發出,不需要等待頁面的 onReady 生命周期之後才去發送請求。這樣可以讓頁面所需的數據儘可能早的處於 Ready 狀態。除了在現有的生命周期發送數據請求以外,我們還提供了prefetch機制(prefetch機制需要在app.json之中進行配置)能夠在小程式框架啟動階段就去請求數據,而不用等待頁面生命周期觸發。
根據小程式被打開的場景,可以對非同步請求進行優先順序排序,不重要的請求放在頁面的 onReady 生命周期去請求。例如,貼吧小程式最經常被訪問的頁面是帖子內容頁,因此除了當前帖子內容以外的數據請求都是非關鍵請求,可以將觸發的時機延後,保證帖子內容儘可能早的被載入出來。
四 清理定時器
當使用swan.navigateTo
進行頁面跳轉的時候,舊頁面是沒有被銷毀的。舊頁面當中定義的定時器仍舊會運行。因此在頁面跳轉的時候,一定要記住清理沒有用的定時器:
Page({
|
五 合理使用自定義組件
自定義組件與模板內的import與include功能都可以達到程式碼復用的效果。需要注意的是,如果自定義組件內沒有邏輯層的功能的話,這時候使用自定義組件就是非必須的了。我們可以用下面的方式實現程式碼的復用:
<import src="./person.swan" />
|
// Person相關函數
|
// 復用person.js中的函數
|
六 漸進式載入
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()移除。
七 白屏優化
當前首頁白屏率
排查異常
小程式白屏數據出現異常上漲時,可以從以下三個方面著手排查分析:
服務穩定性
- 小程式頁面數據請求是否正常:
通過線上巡檢,發現有小程式存在自身服務不穩定的情況。例如小程式頁面數據請求返回4XX,5XX錯誤等。 - HTTPS證書是否存在問題:
排查HTTPS證書是否已過期,導致小程式相關請求失敗,無法展示數據。
有些小程式可能誤使用了自簽的HTTPS證書,由於無法被信任,用戶也無法強制信任,導致頁面數據獲取失敗。
業務邏輯
有些小程式的頁面數據展示可能存在前置條件,例如需要登錄、定位等。在條件不滿足時,可能存在兼容處理問題。這裡給出常見的幾種case:
- 頁面打開時需要首先進行授權,獲取許可權:
授權失敗時需要有響應的兼容邏輯或者給予明確提示。 - 頁面打開時需要登錄才可展示內容:
例如常見的購物類小程式,用戶未登錄時需要有相應的提示,以及觸發登錄的按鈕或者入口。 - 網路連接失敗時,頁面兼容性不足:
這種情況最好是有對應的錯誤頁和重試入口,保證用戶可再操作,提供自主恢復的能力。 - 邏輯中存在自設校驗,校驗不通過:
有些小程式是從微信小程式遷移而來,內部邏輯中可能存在自設的平台檢測校驗等,遷移時或者版本更新時沒有同步變更,導致校驗不通過,從而導致頁面異常。
框架兼容性
小程式框架自身也在不斷更新,所支援的能力也在不斷更新和擴充。同樣,開發者也會對小程式自身也會進行版本更新。這裡就涉及到了兼容性問題。小程式框架版本修復Bug記錄和版本兼容性,請參考以下連接了解和主動規避:
優化性能和體驗
已有啟動性能數據,平均數據和80分位數據較快不一定能保證白屏率就低,白屏case大概率發生在性能的長尾數據中。
從平台跟進的多個小程式白屏數據分析結果來看,小程式白屏率高的主要因素是頁面數據載入和渲染較慢。如果小程式上線後白屏數據就處於高位,或者版本更新後白屏數據上漲,可以通過以下方面進行分析和優化:
- 頁面結構:
部分小程式的頁面內容重度依賴於伺服器的返回,在服務端沒有數據返回的時候,頁面沒有任何內容展示,這樣的情況在遇到網路波動或者服務發生抖動的時候會造成白屏率的陡增。開發者可以在服務端數據返回之前通過動畫,文案體驗上的優化來減小白屏率。 -
頁面數據載入方式:
針對一次請求返回的數據過多的情況,可以從兩個角度來優化: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});
}); -
增加過渡態提示:
頁面載入時,可以使用Loading組件等形式進行提示,給用戶一個提示,提升用戶體驗。 -
使用骨架屏:
骨架屏形式類似下圖,可以很好的提升用戶使用小程式時的體驗。 - 默認態處理:
例如在涉及到定位時,部分小程式會等待定位完成後才展示數據,可以增加默認態數據,定位完成後再更新頁面數據。
總結:
上線四五天後,整體效果有了一定的提升,由於用戶收斂和老用戶更新版本的影響,最終的效果還需再觀察一段時間
通過這次性能優化的調整,體會到了小程式這種運行在端內的程式碼對於性能的苛刻要求,你的每一部分程式碼的簡潔程度,程式碼組織的調整,靜態資源大小、部署、載入的策略
都深深影響到小程式載入的整體指標。
同時,日常生活中的程式碼,也有同樣的問題
1. 對於新的程式碼,是否做到了合理的書寫?拆分?便於維護?
2. 對於冗餘的程式碼,實際上嚴重影響著程式碼整體載入效率,是否有目的的清理和調整?
3. 對於靜態資源,一些圖片、字體,圖片有沒有壓縮合併?冗餘的一張圖片有可能比冗餘的一個jquery更大?
4. 對於舊的api,拿vue2.0舉例,在v-for的時候,如果把key添加上,將大大提升Vnode diff的效率,這塊是否有意識的去優化
5. 很多東西,實際上我們都知道應該去做,是否去主動的做一些,哪怕做一點,哪怕這次去一個 沒用 || 已經 改版了的圖片、js、css(主要)
註:由於內部安全紅線,相關業務資訊已隱藏,轉載請著名出處(https://www.cnblogs.com/Sherlock09/p/11726885.html)