天天看點

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

項目本身包/第三方腳本比較大。

JavaScript 執行阻塞頁面加載。

圖檔體積大且多。

特别是對于首屏資源加載中的白屏時間,使用者等待的時間就越長,使用者感覺到頁面的速度就越慢。麻省理工學院的 Richard Larson 在講話中指出,“人類将被動等待高估了 36%”(https://mazey.cn/t/em)。這意味着使用者感覺到的等待時間比開發工具記錄的長得多。

頁面結構不斷調整,不連貫。抖動的頁面往往讓使用者感覺很卡。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

首先通過 Webpack 插件 <code>webpack-bundle-analyzer</code> 分析出項目中用到的 NPM 包及大小。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

結合項目可以分析出哪些包可以去除,哪些包可以有更好的替代品。

名稱

體積大小(Parsed)

說明

優先級

信心指數

mint-ui

96.05KB

目前引入全部元件,需要按需加載

⭐️⭐️⭐️⭐️

moment

95.51KB

時間戳格式化的庫,因為無法按需加載,目标是替換為可按需加載的 <code>date-fns</code>

quill

213.31KB

富文本編輯器

⭐️⭐️⭐️

whatwg-fetch

9.25KB

原生 <code>fetch</code> 的墊片,已存在 <code>axios</code>,需要統一

⭐️

ua-device

148.48KB

使用 Navigator 代替

⭐️⭐️⭐️⭐️⭐️

assets

546.11KB

整個項目的小體積圖檔,大部分需要替換成連結引入或者分子產品按需加載

然後在項目中移除或替換無用包,以及部分包的按需加載。

<code>mint-ui</code> 按需加載示例:

不影響頁面主邏輯的外鍊往往不是很穩定,一定要等首屏加載完成以後按需加載。

示例:

一般來說尺寸越大,圖檔品質越高,則體積越大;相應的減少圖檔的尺寸體積會變小,但品質也會變差一些,這裡就需要按照産品需求在性能和體驗上尋求一個平衡。

以一個尺寸 400x400 的 GIF 圖為例,尺寸轉為 200x200 之後,體積由 700k 減少到 238k(-66%)。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

GIF 作為一個存在了長達 20 年的格式,相容性當然是最好的,但是其體積和品質對比現在流行的其他格式已經沒啥優勢了。目前動圖常見的表現格式是 APNG、WebP。

APNG(Animated Portable Network Graphics)

基于 PNG(Portable Network Graphics)格式擴充的一種動畫格式,增加了對動畫圖像的支援,同時加入了 24 位圖像和 8 位 Alpha 透明度的支援,這意味着動畫将擁有更好的品質,其誕生的目的是為了替代老舊的 GIF 格式,但它目前并沒有獲得 PNG 組織官方的認可。APNG 被 Mozilla 社群所推崇,2008 年首次在 Mozilla Firefox 中獲得支援,2017 年 Google Chrome 開始支援 APNG,截止到現在主流浏覽器中隻有微軟家的 IE 和 Edge 不支援 APMG。

WebP

最初在2010年由 Google 釋出,目标是減少檔案大小,但達到和JPEG格式相同的圖檔品質,希望能夠減少圖檔檔在網絡上的發送時間。WebP 有靜态與動态兩種模式。動态WebP(Animated WebP)支援有損與無損壓縮、ICC 色彩配置、XMP 诠釋資料、Alpha 透明通道。現在主流浏覽器中隻有 Google Chrome 和 Opera 支援 WebP。

以一個 GIF圖 為例,格式轉為 WebP 之後,體積由 238k 減少到 133k(-44%)。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

但是 133k 的體積依舊很大,讓人難以接受。作為動畫效果,隻要讓視訊循環播放,就能達到和 GIF 一樣的效果,然後我又試了主流的 MP4、WebM。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

在轉成 WebM(同樣是 Google 家的視訊格式)之後,體積由 238k 減少到 40k(-83%)。在使用過程中加上循環播放,去除控件和加載完成後再渲染就達到了和 GIF 一樣的視覺效果。

圖檔上傳前先通過工具壓縮下(例如:https://tinypng.com/),正常都會有 50~80% 的減少。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

PNG/JPG 轉 WebP 後圖檔體積減少了 4-7 倍。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

很多矢量編輯器在導出 SVG 檔案的時候,會附帶很多備援資訊。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

經過 SVGO 類工具壓縮之後,體積往往會縮減約 30%。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

在項目中可以使用 Webpack svgo-loader 自動壓縮。

大量業務上的埋點上報會阻塞圖檔加載,保證首屏渲染完成後再執行上報。

頁面中使用到的各種資源的域名較多,使用 <code>preconnect</code> 可以提前解析 DNS、TLS 協定、TCP 握手,節約後面加載資源時的網絡請求時間。

浏覽器加載頁面時,若沒有指定 <code>icon</code>,會預設請求一個根目錄下的 <code>favicon.ico</code> 檔案,作為手機内嵌的 H5 頁面,往往不需要展示圖示,為了節約這個請求可以通過在 <code>&lt;head&gt;</code> 裡面加上 <code>&lt;link rel="icon" href="data:;base64,="&gt;</code> 禁掉 <code>favicon.ico</code> 網絡請求,畢竟弱網條件下,一個網絡請求相當于 500ms。

Gzip 是一種用于檔案壓縮與解壓縮的檔案格式。原本是 UNIX 系統的檔案壓縮,後來逐漸成為 Web 最流行的資料壓縮格式。它基于 Deflate 算法,可将檔案無損壓縮地更小,對于純文字檔案,大概可以縮減 60% 的體積,進而實作更快的網絡傳輸,特别是對移動端非常重要。目前主流浏覽器普遍地支援 Gzip,這意味着伺服器可以在發送檔案之前自動使用 Gzip 壓縮檔案,而浏覽器可以在接收檔案時自行解壓縮檔案。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

Google 認為網際網路使用者的時間是寶貴的,他們的時間不應該消耗在漫長的網頁加載中,是以在 2015 年 9 月 Google 推出了無損壓縮算法 Brotli,特别側重于 HTTP 壓縮。Brotli 通過變種的 LZ77 算法、Huffman 編碼以及二階文本模組化等方式進行資料壓縮,與其他壓縮算法相比,它有着更高的壓縮效率。針對常見的 Web 資源内容,Brotli 的性能相比 Gzip 提高了 17-25%。

除了 IE、Opera Mini 和百度浏覽器,所有的主流浏覽器都已經支援 Brotli。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

頁面加載中添加骨架圖,骨架圖根據頁面基本架構生成,相對于純白屏,體驗更好。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

圖檔加載的時候設定占位圖,提醒使用者這邊會加載圖檔,不至于很突兀。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

配合 <code>v-lazy</code> 實作示例:

懶加載示例:

首屏占位小圖示直接轉 Base64,必要子產品設定高度,規避整個頁面的抖動。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

桌面端 PWA 應用:

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

移動端添加到桌面:

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

PWA(Progressive Web App - 漸進式網頁應用)是一種理念,由 Google Chrome 在 2015 年提出。PWA 它不是特指某一項技術,而是應用多項技術來改善使用者體驗的 Web App,其核心技術包括 Web App Manifest、Service Worker、Web Push 等,使用者體驗才是 PWA 的核心。

PWA 主要特點如下:

可靠 - 即使在網絡不穩定甚至斷網的環境下,也能瞬間加載并展現。

使用者體驗 - 快速響應,具有平滑的過渡動畫及使用者操作的回報。

使用者黏性 - 和 Native App 一樣,可以被添加到桌面,能接受離線通知,具有沉浸式的使用者體驗。

PWA 本身強調漸進式(Progressive),可以從兩個角度來了解漸進式,首先,PWA 還在不斷進化,Service Worker、Web App Manifest、Device API 等标準每年都會有不小的進步;其次,标準的設計向下相容,并且侵入性小,開發者使用新特性代價很小,隻需要在原有站點上新增,讓站點的使用者體驗漸進式的增強。相關技術基準線:What makes a good Progressive Web App?。

站點需要使用 HTTPS。

頁面需要響應式,能夠在平闆和移動裝置上都具有良好的浏覽體驗。

所有的 URL 在斷網的情況下有内容展現,不會展現浏覽器預設頁面。

需要支援 Wep App Manifest,能被添加到桌面。

即使在 3G 網絡下,頁面加載要快,可互動時間要短。

在主流浏覽器下都能正常展現。

動畫要流暢,有使用者操作回報。

每個頁面都有獨立的 URL。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

通路位址:https://bbs.mihoyo.com/bh3/

PWA:僅支援在 IOS 端添加到桌面。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)
前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

通路位址:https://m.aliexpress.com/

PWA:使用 Google Workbox(CDN)

支援添加到桌面,manifest。

支援緩存,Service Worker。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

通路位址:https://h5.ele.me/msite/#pwa=true

PWA:自研 - PWA 在餓了麼的實踐經驗

支援緩存和離線通路,Service Worker。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)
前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

左邊原生應用,右邊 PWA

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

通路位址:https://www.instagram.com/

PWA:使用 Google Workbox

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

通路位址:https://mobile.twitter.com/home

PWA:Twitter 自研 - How we built Twitter Lite

除了正常的靜态資源以外,Twitter 把首頁也緩存了下來。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

離線狀态下有很好的使用者體驗,而不是顯示預設的浏覽器頁面。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

Workbox 是一組庫,可以幫助開發者編寫 Service Worker,通過 CacheStorage API 緩存資源。當一起使用 Service Worker 和 CacheStorage API 時,可以控制網站上使用的資源(HTML、CSS、JS、圖像等)如何從網絡或緩存中請求,甚至允許在離線時傳回緩存的内容。

Workbox 是由許多 NPM 子產品組成的。首先要從 NPM 中安裝它,然後導入項目 Service Worker 所需的子產品。Workbox 的主要特性之一是它的路由和緩存政策子產品。

路由和緩存政策

Workbox 允許使用不同的緩存政策來管理 HTTP 請求的緩存。首先确定正在處理的請求是否符合條件,如果符合,則對其應用緩存政策。比對是通過傳回真值的回調函數進行的。緩存政策可以是 Workbox 的一種預定義政策,也可以建立自己的政策。如下是一個使用路由和緩存的基本 Service Worker。

這個 Service Worker 使用一個網絡優先的政策來緩存導航請求(用于新的 HTML 頁面),當它狀态碼為 <code>200</code> 時,該政策将緩存的頁面存儲在一個名為 pages 的緩存中。使用 Stale While Revalidate strategy 緩存 CSS、JavaScript 和 Web Worker,将緩存的資源存儲在一個名為 assets 的緩存中。采用緩存優先的政策來緩存圖像,将緩存的圖像存儲在名為 images 的緩存中,30 天過期,并且一次隻允許 50 個。

預緩存

除了在送出請求時進行緩存(運作時緩存)之外,Workbox 還支援預緩存,即在安裝 Service Worker 時緩存資源。有許多資源是非常适合預緩存的:Web 應用程式的起始 URL、離線回退頁面以及關鍵的 JavaScript 和 CSS 檔案。

使用一個支援預緩存清單注入的插件(webpack 或 rollup)來在新的 Service Worker 中使用預緩存。

這個 Service Worker 将在安裝時預緩存檔案,替換 <code>self.__WB_MANIFEST</code>,其中包含在建構時注入到 Service Worker 中的資源。

離線回退

讓 Web 應用在離線工作時感覺更健壯的常見模式是提供一個後退頁面,而不是顯示浏覽器的預設錯誤頁面。通過 Workbox 路由和預緩存,你可以在幾行代碼中設定這個模式。

如果使用者處于離線狀态,則傳回緩存的離線頁面的内容,而不是生成一個浏覽器錯誤。

有了 Workbox,可以利用 Service Worker 的力量來提高性能,并給您的站點提供獨立于網絡的優秀的使用者體驗。

自研 Service Worker 更加靈活、可控,但是因為需要考慮到各種相容,研發成本較高。

安裝 Workbox:

按照引導配置 <code>workbox-config.js</code>:

根據配置生成 Service Worker 程式:

由于實際靜态資源是挂載在 CDN 上面,需要修改預渲染資源的字首。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

Workbox CLI - generateSW - Configuration

更多緩存配置可查閱官方文檔。

安裝:

Webpack 配置:

頁面中觸發 Service Work:

為網站配置開屏圖檔、狀态欄等。

開屏圖檔尺寸總結:

螢幕尺寸

倍數

圖檔尺寸

1024x1366(512x683)

x2

2048x2732

834x1194(417x597)

1668x2388

768x1024(384x512)

1536x2048

834x1112(417x556)

1668x2224

810x1080

1620x2160

428x926(214x463)

x3

1284x2778

390x844

1170x2532

375x812

1125x2436

414x896

1242x2688

828x1792

414x736

1242x2208

375x667

750x1334

320x568

640x1136

用戶端在頁面首次加載後把資源緩存下來,之後每次加載不進行網絡請求直接讀取緩存,然後再對比本次請求的版本和線上的版本,若有更新再次緩存以供下次通路,極大的縮短白屏時間。缺點是有滞後性,永遠落後于線上一個版本。

為了解決用戶端緩存的滞後問題,離線包方式是一種提前下載下傳頁面資源的方式。缺點是占用使用者更多的流量,優點是能夠實作真正意義上的頁面“秒開”。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

首屏動态渲染受制于後端接口傳回的資料,如果接口存在體積大、有前後依賴關系、數量多需要耦合等問題,首屏渲染因為等待資料往往會比較慢。解決辦法是拉上後端一起梳理下哪些資料才是首屏所需要的,用一個接口把首屏資料輸送給前端。

在浏覽器控制台的 Performance 欄位,可以記錄整個頁面生命周期的每一個細節,其中有大量描述 JavaScript 堆棧記憶體占用的情況。

前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)
CPU memory is attached to the CPU, and is almost universally two DIMMs wide (128b), and is a multi-drop bus (so requires more power and conditioning to drive, even at lower clocks.) Of course, we generally expect to be able to configure CPU memory by snapping in different DIMMs, so the CPU’s memory controller is far more complicated and flexible.

JavaScript 對記憶體的占用受代碼的影響,如果在運作時緩存和計算大量的資料、處理巨量字元串等耗費空間的行為,那麼記憶體就會極速飙升,極端情況下會導緻承載網頁的應用閃退。

GPU memory is attached to the GPU, and is a wider interface, with shorter paths and a point-to-point connection. As a consequence, it generally runs at higher speed (clock) than CPU memory. It’s common for GPU memory to deliver several hundred GB/s to the GPU; for a CPU, it’s in the mid tens of GB/s. (There are higher-end CPUs with very wide interfaces that are around 100 GB/s.) The internal design of both kinds of memory is very similar.
前端性能和加載體驗優化實踐(附:PWA、離線包、記憶體優化、預渲染)

經由我自測,這部分記憶體受螢幕尺寸和幀數影響較大,如果是動畫或高精度的圖檔渲染時,則記憶體會向上浮動。

動态渲染的頁面,首屏需要等待 JavaScript 加載完成之後才能執行渲染,等待 JavaScript 加載的時間越久,白屏的時間越久。而通過在 CI/CD 階段,将傳統 SSR 的流程執行一遍,用動态生成的 <code>index.html</code> 覆寫原來“空的”<code>index.html</code>,即優化了首屏加載體驗,省去了骨架屏的步驟,也提升了加載速度。使用 prerender-spa-plugin 可以輕松配置預渲染頁面,現已經被 React/Vue 項目廣泛應用。

參考

Resource Hints – What is Preload, Prefetch, and Preconnect?

漸進式 Web 應用(PWA) | MDN

What is the difference between GPU memory and CPU memory?

使用記憶體性能分析器檢視應用的記憶體使用情況

版權聲明

本部落格所有的原創文章,作者皆保留版權。轉載必須包含本聲明,保持本文完整,并以超連結形式注明作者後除和本文原始位址:https://blog.mazey.net/2548.html

(完)

GitHub:mazeyqian

Blog:blog.mazey.net

繼續閱讀