1. 背景
3月份針對線上重點H5項目秒開進行治理,本文將逐步介紹如何通過H5頁面的優化手段來提高 1.5 秒開率。
2. 爲什麼要優化
-
從用戶角度看,優化能夠讓頁面加載得更快、對用戶操作響應更及時,用戶體驗更良好,提升用戶體驗和降低用戶流失率非常重要。其中 Global Web Performance Matters for ecommerce報告中也有具體說明優化的重要性。
-
從企業角度看,優化能夠減少頁面請求數或者減小請求所佔帶寬,能夠節省可觀的資源成本,最終提高收益轉化。
3. 優化目標
從上圖中可以看出,有些域名下可能低於90%,最高的也沒達到96%,離既定98%的目標還有一定差距。
4. H5性能分析
分析工具
-
Lighthouse
-
Chrome DevTools
-
gtmertrix 在線可視化分析工具 https://gtmetrix.com/
Webview加載H5
通常情況分以下幾個階段
-
Webview初始化。
-
到達新的頁面,網絡連接,從服務器下載html,css,js,頁面白屏。
-
頁面基本框架出現,js請求頁面數據,頁面處於loading狀態。
-
出現所需的數據,完成整個頁面的渲染,用戶可交互。從圖形直觀看H5 啓動過程:
如何縮短這些過程的時間,就成了優化 H5 性能的關鍵。接下來我們詳細看一下各個階段注意的優化點。
優化方案
從以下幾個方面入手:
-
加載策略優化
-
增加骨架屏
-
資源請求優化(靜態資源、圖片以及 webp 、圖片懶加載、組件按需加載)
-
打包資源優化
-
CDN & 緩存
接下來就逐個分析
(1) 加載策略優化
先看一張圖:
https://developers.google.com/web/fundamentals/primers/modules#module-vs-script
從這張圖裏我們能看到什麼,大致能總結爲以下四點:
-
默認情況:HTML解析,然後加載 JS,此時 HTML 解析中斷,然後執行 JS,最後 JS執行完成並恢復 HTML解析。
-
defer情況下:HTML 和 JS 並駕齊驅,最後才執行 JS( js腳本在所有元素加載完成後執行,而且是按照js腳本聲明的順序執行,但要等到dom文檔全部解析完纔會被執行)。
-
async 情況下:HTML和 JS 並駕齊驅,JS 的執行可能在 HTML解析之前就已完成了 (js腳本是亂序執行的,不管你聲明的順序如何,只要某個js腳本加載完就立即執行)。
-
module情況下:與defer情況類似,只不過在提取的過程中會加載多個 JS 文件而已 (聲明acript標籤type="module"屬性從而擁抱es6的模塊導入導出語法, 加載也和defer差不多,只不過可以加載多個JS文件而已)
項目中實踐示例:
(2) 預加載
prefetch 和 preload
preload 是一個新的 Web 標準,在頁面生命週期中提前加載你指定的資源,同時確保在瀏覽器的主要渲染機制啓動之前。
具體使用如下:
<script rel="prefetch" as="script" href="index.js" />
<script rel="preload" as="script" href="index.js" />
注意:preload 緊挨着 title 放,使其最早介入。
prefetch 是提示瀏覽器,用戶在下次導航時可能會使用的資源(HTML,JS,CSS或者圖片等),因此瀏覽器爲了提升性能可以提前加載、緩存資源。prefetch 的加載優先級相對較低,瀏覽器在空閒的時候纔會在後臺加載。用法與 preload 類似,將 rel 的值替換成 prefetch 即可。
preload 是告訴瀏覽器頁面必定需要的資源,瀏覽器一定會加載這些資源,而 prefetch 是告訴瀏覽器頁面可能需要的資源,瀏覽器不一定會加載這些資源。所以建議:對於當前頁面很有必要的資源使用 preload,對於可能在將來的頁面中使用的資源使用 prefetch。
注意:用 preload 和 prefetch 情況下,如果資源不能被緩存,那麼都有可能浪費一部分帶寬,請慎用。非首頁的資源建議不用 preload,prefetch 作爲加載下一屏數據來用。
dns-prefetch 和 preconnect
dns-prefetch
DNS 請求需要的帶寬非常小,但延遲較高,這點特別是在手機網絡上比較明顯。預讀取 DNS 能讓延遲明顯減少一些(尤其是移動網絡下)。爲了幫助瀏覽器對某些域名進行預解析,你可以在頁面的html標籤中添加 dns-prefetch 告訴瀏覽器對指定域名預解析。
dns-prefetch 是一項使瀏覽器主動去執行域名解析的功能。dns-prefetch 應該儘量的放在網頁的前面,推薦放在後面。具體使用方法如下:
<link rel="dns-prefetch" href="//*.com">
洗車項目中有體現:
注意:dns-prefetch需慎用,推薦首屏加載資源添加DNS Prefetch
preconnect
和 DNS prefetch 類似,preconnect 不僅會解析 DNS,還會建立 TCP 握手連接和 TLS 協議(如果是https的話)。用法如下:
preconnect
允許瀏覽器在 HTTP 請求實際發送到服務器之前建立早期連接。可以預先啓動 DNS 查找、TCP 握手和 TLS 協商等連接,從而消除這些連接的往返延遲併爲用戶節省時間。
<link rel="preconnect" href="//*.com.cn" />
(3) 骨架屏
從圖上可以看出有白屏情況,FCP 時間超過了 1秒多,解決下來就用了骨架屏來解決白屏情況 並提升 FCP。
骨架屏
就是在頁面資源尚未加載完成以及渲染尚未完成時,需要先給用戶的展示頁面大致結構。直到資源加載完成以及渲染完成後,使用渲染的頁面。骨架屏處理方案也很多,常用方案有以下幾種:
-
首屏:可以在index.html模版中手寫骨架屏相關代碼。
-
其他頁面:可以利用UI提供SVG圖
-
作爲SPA中路由切換的loading:需自己編寫骨架屏,推薦兩個成熟方便定製的svg組件去定製骨架屏- react-content-loader和vue-content-loader。
-
骨架圖渲染前不要出現任何網絡請求,在此之前 HTML 內容不要超過 4KB。
我這裏採用了固定的骨架屏SVG打包自動注入到模板方式。併產出了基於vite 的自動化注入骨架屏和無阻塞緩存資源文件@auto/vite-plugin-cdn私有插件。
舉個🌰:
相關鏈接:
-
https://www.npmjs.com/package/react-content-loader react-content-loader -
https://www.npmjs.com/package/vue-content-loader vue-content-loader -
http://npm.corpautohome.com/package/@auto/vite-plugin-cd @auto/vite-plugin-cdn
(4) 資源請求優化
圖片壓縮和webp
圖片是網站性能優化需要重點關注的方向。爲什麼這麼說呢?來看個圖片:
一般 UI 提供的切圖都是未通過壓縮的圖片,所有在開發過程中,我們必須再壓縮一次。如果壓縮後的圖片還是大於 500KB 就要考慮將圖片分割成多張。目前市面上圖片壓縮比較多,給大家推薦個好用的工具(docsmall)。可批量壓縮各類圖片。
WebP 的優勢體現在它具有更優的圖像數據壓縮算法,在肉眼識別無差異的圖像質量情況下帶來更小的圖片體積的優勢;同時具備了無損和有損的壓縮模式、Alpha 透明以及動畫的特性,在 JPEG 和 PNG 上的轉化效果都相當優秀、穩定和統一。內部提供了圖片資源可以上傳到 前端加速服務 或 前端靜態資源服務內部資源庫會自動生成webp格式,可以在項目打包的時候處理圖片時加上 format=webp 即可,接口動態圖片可採用 @auto/img-crop私有包做裁切同時也可通過參數動態支持webp和設置緩存時間。
webp前後對比:
從對比結果看,同圖片採用webp 大小至少減少了 50%,越大的圖優化比例越大。大幅減少了文件體積,縮短了加載的時間,大頁面圖片量較多的場景下,頁面的渲染速度是有較大提升的。
相關鏈接:
-
https://docsmall.com/ docsmall -
http://fs-finder-dev.yz.test.autohome.com.cn/docs/#/ 前端加速服務 -
http://festatic.yz.test.autohome.com.cn/doc/#/md/useful 前端靜態資源服務 -
http://npm.corpautohome.com/package/@auto/img-crop @auto/img-crop
CDN & 緩存
上面提到了前端加速服務 或 前端靜態資源服務內部服務均集成CDN功能。具體情況可以參考使用文檔。
結合以上兩個服務的應用能很好的處理資源問題,目前我們的新 SPA項目都發布到了前端加速服務上。如圖:
資源文件自動都有緩存
未覆蓋的 CDN
從圖上看左圖沒命中緩存,右圖則命中緩存,很多項目由於域名接口和網頁接口一樣CDN 就是沒開啓緩存,我們後通過域名Path 來針對開啓 CDN緩存。
(5) 打包資源優化
提取第三方庫
通常情況下,大多第三方庫的代碼不做版本升級是不會發生變化的 ,這時就可以用到 DllPlugin:把複用性較高的第三方庫打包在一起,不升級就不需要重新打包。
這樣做的優點:
-
提取的第三方庫生成的資源版本號(資源的訪問連接)不會變,提高了緩存的利用;
-
避免打包出單個文件的大小太大,不利於加載;
-
每次構建只重新打包業務代碼,提高打包效率。
爲了讓前端頁面性能更優, App WebView 中針對 React
、Vue
、Zepto
三大常用框架相關資源及 Polyfill
進行了預加載處理,所以我們把這些固定的資源調整爲無阻塞的預加載地址。具體如何使用 App H5提供了 webpack的相關配置說明。
這裏針對 vite 的配置做些說明:
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import legacy from '@vitejs/plugin-legacy';
import createExternal from 'rollup-plugin-external-globals';
import cdn from '@auto/vite-plugin-cdn';
export default ({ mode }) => {
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
const { VITE_USER_NODE_ENV = 'mock' } = process.env;
const plugins: Array<any> = [];
const isProduction = process.env.NODE_ENV === 'production';
if (isProduction) {
// 設置預加載的 react 等包爲 external
plugins.push(
createExternal({
react: 'React',
'react-dom': 'ReactDOM',
history: 'HistoryLibrary',
'react-router': 'ReactRouter',
'react-router-dom': 'ReactRouterDOM',
immer: 'immer',
axios: 'axios',
'js-cookie': 'Cookies',
}),
);
plugins.push(
cdn({
enableModule: true,
}),
);
}
// https://vitejs.dev/config/
return defineConfig({
legacy({
targets: ['> 0.05%', 'not dead', 'not op_mini all'],
}),
...plugins,
],
build: {
rollupOptions: {
external: [
'react',
'react-dom',
'history',
'react-router',
'react-router-dom',
'axios',
'js-cookie',
],
},
},
});
};
示例:
我們來看一組圖:
從圖上看優化前後,文件數從295 個減少到 214 個, 大小從 1.63MB 減少到439.88KB,大小降了73.6460%
webpack 和 vite 配置
設置預警來檢驗打包文件
資源(asset)是從 webpack 生成的任何文件。此選項根據單個資源體積(單位: bytes),控制 webpack 何時生成 性能提示。用法:
// webpack 設置單個靜態資源文件的大小最大超過300KB則給出警告
module.exports = {
// ...
performance: {
maxAssetSize: 1024 * 300
}
};
// vite設置
build: {
chunkSizeWarningLimit: 300 // 塊大小警告的限制 (以kbs爲單位)默認500
}
5. 總結
經過以上的一系列的優化實施,我們來看一下優化前後數據的對比:
從2月底開始實施優化,上圖可以很明顯看出數據的變化,秒開率雖然已經做到了95%以上,達到 98%的只有個別項目,還需要在迭代過程中關注性能以及持續的優化,這裏也感謝爲H5頁面秒開做出貢獻的同學。
請查看這些優化指南以獲取更多信息:
-
Global Web Performance Matters for ecommerce
-
https://www.keycdn.com/support/preconnect
-
async-defer-module
PPT視頻講解
作者|黃繼華