我所知道的 Web 性能優化策略

前言

本文核心分爲兩部分,第一部分講述普通瀏覽器中能幹的事情,第二部分則講述在自建容器的背景下更能幹的事情。html

文章內容會比較粗略,若是你對具體實現感興趣,歡迎在 留言。前端

1、止步於瀏覽器

1.1 DNS Prefetch

一般狀況下,一個 html bundle 裏面通常會有 script 等標籤去加載其餘的資源。瀏覽器在加載完 html 以後,就會去加載 script 等標籤裏面的內容,大多狀況下,這種標籤裏 uri 的 host 和當前頁面的每每是不相同的,那就會涉及到 DNS 解析的問題,會有必定程度的損耗。git

在 HTML 裏面加入 DNS Prefetch 則會讓瀏覽器提早進行 DNS 的解析而且緩存到系統中,這樣就能夠提高網頁的加載效率了。github

<link rel="dns-prefetch" href="//img.alicdn.com">
複製代碼

1.2 域名收斂

在像 v2ex 這樣的社區論壇中,每每會有不少用戶外鏈圖片,不一樣的圖片有不一樣的域名。這個時候 DNS Prefetch 會顯得很無力,若是說在圖片上傳以後作必定的 轉化 或者 同步,把域名收斂到同一個域名中,這樣就能彌補相關的缺憾了。web

aaa.com/jjj.png ->  mycdn.com/aaa/jjj.png
bbb.com/jjj.png ->  mycdn.com/bbb/jjj.png
複製代碼

1.3 加載合適的圖片

同一個圖片在 在不一樣的設備、不一樣的網絡條件、不一樣的渲染面積下,統一加載同樣的尺寸不免是 「奢侈」 的,利用 CDN 裁剪 + 前端嗅探 去加載不一樣體積、不一樣壓縮比率、不一樣格式的圖片,能在節省很多流量的同時提高很大的性能。算法

目前主流 CDN / OSS 都是支持經過 URL 後綴進行圖片裁剪的,好比:阿里雲小程序

http://image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/crop,x_100,y_50
複製代碼

在前端嗅探上,通常咱們能夠嗅探 是否支持 webp、客戶端分辨率、當前網絡情況,去全局設置不一樣的圖片加載 URL。微信小程序

base6四、瀏覽器

1.4 不要展現 「絕對Loading / 佔位」

當前時代,大部分網頁的數據都是動態下發的,甚至千人前面,爲了減小用戶等待的焦灼感,每每會設置一個 Loading 動畫或者 骨骼圖佔位。但當請求響應足夠快的時候,會發現這種 Loading 或 佔位 卻會給人相反的感受 —— 瞬間的閃動。緩存

針對這種狀況,在使用 Loading 或骨骼圖佔位的,能夠作必定的優化,好比請求發起後 200ms 以上還未返回數據才展現佔位圖。在 React Suspense 中,爲了這種效果官方甚至加了一個 API。

1.5 資源 combo

在 HTTP 請求中,請求建立每每由於 TCP 三次握手等會有一個很是大的開銷。涉想在 HTTP 1.0 時代,若是頁面上有 50 個 Script 標籤,會有 50 次的請求建立,在資源不是那麼大的狀況下,請求建立的耗時每每會遠大於資源真正的下載時間。

在服務端作資源 Combo,而後再往 CDN 回源,就能很大程序上減小這種開銷。

xx.com/a.js
xx.com/b.js
xx.com/c.js
xx.com/d.js

xx.com/combo?a.js,b.js,c.js,c.js
複製代碼

在 HTTP 2.0 時代資源要不要 Combo + 多少個資源(多大的體積)Combo 到一塊兒 是個有意思的話題,這裏不作討論。

1.6 在線 Shim

前端由於瀏覽器問題一直存在店鋪這種東西,有不少的特性每每在新的瀏覽器版本里面已經支持,但奈何老的不支持須要作墊片。若是對代碼統一加墊片則又會讓新瀏覽器很尷尬(我要這新特性有何用?),使用在線的 Shim 應該是一個不錯的 Shim。

好比著名的 polyfill.io/v3/url-buil… 就是一個這樣的服務。在支持 Object.assign 中訪問 polyfill.io/v3/polyfill… , 會獲得:

/* Disable minification (remove `.min` from URL path) for more info */
複製代碼

而在不支持的瀏覽器中訪問,會獲得:

(function(undefined) {if (!(// In IE8, defineProperty could only act on DOM elements, so full support
// for the feature requires the ability to set a property on an arbitrary object
'defineProperty' in Object && (function() {
	try {
		var a = {};
		Object.defineProperty(a, 'test', {value:42});
		return true;
	} catch(e) {
		return false
	}
}()))) {!function(e){
...
複製代碼

在國內也有這樣的服務,好比:polyfill.alicdn.com/polyfill.mi… ,若是你以爲不夠信賴,能夠自建而後部署在 CDN 上。

1.7 分離靜態資源

在絕大多數狀況下,訪問靜態資源的時候並不須要知道 Cookie 信息,爲靜態資源使用一個新的域名能有效避免用不着的 Cookie 上傳,能減小一部分無用流量的傳輸。

1.8 使用 requestAnimationFrame 實現 60 FPS 動畫

在繪製動畫的時候,優先使用 requestAnimationFrame,會充分利用分利用顯示器的刷新機制,實現 60 FPS 的感受。

1.9 節流 & 防抖

在 Web 中,像 Scroll 這種事件,在界面操做中觸發的頻率是很是之高的。涉想這樣的一種場景:用戶往下滑動網頁,當滑動距離超過 1000px 的時候,右下角展示一個 回到頂部 的按鈕。想固然的操做就是監聽 Scroll 事件,當值大於 1000px 的時候展現 按鈕,但由於 Scroll 的高頻率觸發,尤爲在移動端這樣作就能感受到比較明顯的性能問題了,若是咱們對其加個操做 —— 1s 內檢測函數只觸發 1 次 或者在用戶停下來的時候再去檢測位置。這樣頁面總體就會流暢不少了,相對應的兩個操做就是 節流 和 防抖。

在 Web 開發過程當中,對於這種高頻次觸發的事情,合理的進程節流和防抖能在很大程度上增長頁面流產度。

1.10 使用新版本 JavaScript

通常狀況下,V8 等 JavaScript 的 Runtime 都會對新特性有優化,在新瀏覽器上使用 Babel 轉化後的代碼不免會有必定的浪費與惋惜。在新瀏覽器上使用新預發,老瀏覽器上使用老語法,纔是比較好姿式。

實現上一種思路就是在線 Shim;第二種思路是正對先加載 seed 再加載 bundle 代碼,能夠在加載 bundle 以前作一個知否支持新版本 ES 的判斷(好比是否支持 async 函數),而後再加載相應的 Bundle。

1.11 性能測量

window.performace 能展現絕大多數檢測 Web 性能的指標,在業務代碼中埋點收集 window.performance 的值,能夠爲網頁性能短板作很好的測量與統計。

1.12 善用 LocalStorage

在一些場景下,每次用戶進入時數據的變化不會太大,好比不怎麼更新的我的博客頁面。這個時候就可使用 LocalStorage 去作 HTML 的緩存,頁面進入的時候直接從 Storage 中獲取緩存,而後 append 到頁面上,等接口數據回來以後,再 Diff 作更新。

在新版本瀏覽器中,能夠用 indexedDB 等代替 LocalStorage。

1.13 組件級緩存

在 SPA 網站中,加載 bundle 大體上能夠分爲兩份:

  1. 全部的組件代碼和業務代碼打到一塊兒,和業務代碼一塊兒輸出
  2. 組件代碼在組件內部各自打包,業務代碼打包的時候 external 掉組件代碼,最後 combo 到一塊兒輸出。

針對第二種狀況,能夠利用 LocalStorage 等單獨緩存組件代碼(帶上版本號),在端側實現一個 Combo 的機制(有 Cache 取 Cache,沒 Cache Fetch),這樣一來,就能讓一個網站的多個頁面享受同份緩存,讓之第一次也能很是快速的訪問。

1.14 GZIP & BBR

GZIP 壓縮使用 Deflate 能有效壓縮文本資源的大小,在現代瀏覽器中,對 GZIP 的支持也是很是良好。值的注意的是,GZIP 的壓縮並非壓的越小越好,過小會產生壓縮性能的問題。

傳統 TCP 使用的是基於丟包的擁塞控制算法,但並非全部的丟包都是網絡堵塞所致使的,爲此 Google 開發了 BBR 擁塞控制算法,能有效提高服務器的吞吐量,若是服務器支持的話,能夠開啓 BBR 來加快網絡傳輸。

1.15 Service Worker

Service Worker 本質上充當Web應用程序與瀏覽器之間的代理服務器,利用 Service Worker 能夠極致的控制每一個請求,進而能夠對 Web APP 在瀏覽器上作離線處理。

傳說中的 PWA 就是對這個東西的一個極致應用。

1.16 WebWorker

WebWorker 爲 JavaScript 在瀏覽器中多線程調用提供了能力,可讓主線程建立 Worker 線程,針對一些密集計算或者須要時間比較高的場景,是很是有效的。好比:網頁版郵箱附件上傳等。

2、外探於自建容器

2.1 WKWebview

從 iOS8 開始,iOS 提供了 WKWebview 來代替 UIWebview。相比於 UIWebview,在性能和內存控制上都有很是大的提高,固然問題也是有的,好比 Cookie 同步問題等,但坑總能趟過去。

回到優勢上,WKWebview 給前端最直接的體驗莫過於 「Scroll 終於再也不須要滾動中止下來才能觸發了」,進而 LoadMore 等會更加的流暢。

2.2 Webviw 內核內置

Android 的碎片化一直是一個很嚴重的問題,即便是在今天。經過內置高性能的 Webview(好比 U四、X5)等,會爲整個 APP Web 頁面提速很多,在兼容性方面,也會好不少。

2.3 資源 Cache

要實現頁面秒開效果,Cache 確定是第一優先級,經過下發離線包,讓頁面上的資源(HTML,CSS)離線,就能夠很大程度上提高頁面的性能。

在離線體系建設上,主要有兩點須要考慮:1. 離線若是能快速下發,快速覆蓋新版本 2. 前端如何才能無感知接入離線體系。

針對第一個問題,每每採起推+拉結合的方式;針對第二個問題,在實現的時候採用攔截網絡的策略,就能夠避免前端對離線的感知了

2.4 代理請求

如前文所述,建立一個 HTTP 請求是很是耗時的,從客戶端的角度來看,是能夠去優化這種請求的,好比被普遍使用的 Spidy。

在 WebAPP 中,發出一個數據請求,讓走容器的經過而非 WebView 的通道,不只有機會能讓速度變快,同時還能夠進行相應的加密,讓抓包者懵逼。

2.5 數據預加載

試想一個網頁的加載過程,加載 HTML -> 加載 JS -> 執行 JS -> 請求數據 -> 再次渲染。請求數據的流程是比較靠後的,在 WebAPP 中,若是能讓這個請求或者請求到的數據提早,則會進一步提高網頁速度。

一個比較經常使用的方式是往客戶端下發一份配置,標識 頁面地址、請求入參、緩存時間等信息,讓客戶端在訪問這個 URL 的時候去加載數據,等前端代碼真正請求的時候,直接拿客戶端提早請求到的數據;或者說在前一個頁面調用接口通知客戶端去請求相關的接口。

目前這種作法應用比較普遍,好比:微信公衆號,UC Feed 流等。

2.6 閹割版 WebView

WebView 由於各類歷史緣由,「慢」 一直徘徊在他的左右。去定製 WebView,甚至去實現一層上層 DSL,只保存比較優秀的部分,也能在限制部分場景的同時去提高頁面性能。

最典型的設計就是微信小程序了,閹割版的 WebView + Cache 機制,很難讓人會以爲這是一個 WebAPP

2.7 Weex/React Native

Weex/RN 相比於閹割版 WebView,是一個更加激進的優化訪問。核心原理是本身造一個高性能的 Runtime,和客戶端高度配置,由 JS Bundle 去 call native,用 Native 的方式去渲染頁面。

同時對於 Web 上性能上有問題的部分,能夠用傳入表達式的方式讓 Native 去執行。好比註明的 BindingX,就是傳入一個表達式,而後去客戶端去執行這個表達式,進而避免了頻繁 call native 的問題,這樣就能夠保證動畫的流暢運行了。

2.8 Flutter

...

原文地址:github.com/rccoder/blo…

相關文章
相關標籤/搜索