H5頁面秒開優化與實踐

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

通常情況分以下幾個階段

      1. Webview初始化。

      2. 到達新的頁面,網絡連接,從服務器下載html,css,js,頁面白屏。

      3. 頁面基本框架出現,js請求頁面數據,頁面處於loading狀態。

      4. 出現所需的數據,完成整個頁面的渲染,用戶可交互。從圖形直觀看H5 啓動過程:
      5. 圖片

如何縮短這些過程的時間,就成了優化 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 中針對 ReactVueZepto 三大常用框架相關資源及 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',
        ],
       
      },
    },
  });
};
這裏@auto/vite-plugin-cdn私有插件中提供正常骨架屏、預加載資源、處理資源加載順序

示例: 

圖片

圖片

我們來看一組圖:

圖片

圖片

從圖上看優化前後,文件數從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
}
將打包後的靜態資源控制在 300KB 以內,最終通過 Gzip 壓縮後,基本都在 100KB 以內。其他的優化包括提取第三方庫、移除調試和無用代碼、Tree Shaking 等。

5. 總結

經過以上的一系列的優化實施,我們來看一下優化前後數據的對比: 

圖片

從2月底開始實施優化,上圖可以很明顯看出數據的變化,秒開率雖然已經做到了95%以上,達到 98%的只有個別項目,還需要在迭代過程中關注性能以及持續的優化,這裏也感謝爲H5頁面秒開做出貢獻的同學。

請查看這些優化指南以獲取更多信息:

 

    • Global Web Performance Matters for ecommerce

    • https://www.keycdn.com/support/preconnect

    • async-defer-module

PPT視頻講解 

作者|黃繼華

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章