常見 Web 效能優化方式
這篇文章是我閱讀Web Performance 101 之後的進行的粗糙的翻譯作為筆記,英語還行的童鞋可以直接看原文。
這篇文章主要介紹了現代 web 載入效能(注意不涉及程式碼演算法等),學習為什麼載入效能很重要、有哪些優化的方法以及有哪些工具可以幫助我們對網站進行優化。
為什麼效能優化很重要?

首先,載入緩慢的網站讓人很不舒服!
最明顯的例子就是當一個移動網站載入太慢的時候,使用者體驗如同觀看一部恐怖電影。
圖片來源:Luke Wroblewski

第二,網站效能直接影響你的產品質量。
—— 2016 年,AliExpress 將他們網站的效能提升了三分之一,然後他們收到的訂單增加了 10.5%!
——2006 年,谷歌曾經嘗試將他們的搜尋放慢 0.5 秒然後發現使用者的搜尋(請求)次數減少了 25%。
——2008 年,Aberdeen 集團發現將網站放慢 1s,會導致使用者滿意度下降 16%。
此外還有一系列如上的資料,不管是新的還是舊的:(wpostats.com· pwastats.com)。
這就是為什麼網站效能很重要。

現在,我們需要弄懂當我們說一個網站很快意味著什麼。
在什麼情況下可以說一個網站很快?
——它必須載入很快(檔案下載、介面渲染),
——然後,在載入之後,它必須很快的執行(比如動畫不跳幀、滾動很絲滑)。

網站載入很快意味著:
——伺服器對於客戶端請求響應很快,
——網站自身載入渲染很快。

在這篇文章中,我們將會討論這個因素:如何讓網站快速載入以及渲染。
有哪些效能優化方式?
JavaScript
一、壓縮程式碼
先從 JavaScript 開始吧。通常情況下,JavaScript 是網站載入緩慢的根源。

第一種 JavaScript 優化方式是壓縮,如果你已經知道了的話,直接跳過吧。
什麼是壓縮?在一般情況下,人們寫 JavaScript 程式碼會使用一種方便的格式,包含縮排、富有含義的長變數名、寫註釋等等。因為這種方式,程式碼具有很高的可讀性,但是多餘的空格和註釋會使得 JavaScript 檔案變得很大。

為了解決這個問題,人們想到了程式碼壓縮。在壓縮的過程中,程式碼會被去掉所有不必要的字母,替換成短的變數名,去掉註釋等等。在最後,程式碼檔案變得比之前更小,但是程式碼的功能並不受影響。
程式碼壓縮可以將程式碼檔案減小大約 30% ~ 40%。

主流的程式碼打包工具都支援程式碼壓縮:
—— mode: production
in webpack,
—— babel-preset-minify
in Babel,
—— gulp-uglify
in Gulp
二、使用 async
和 defer

接下來,你寫了一個 JavaScript 指令碼,然後進行了壓縮,現在想要在頁面中載入它。該如何做呢?

最簡單的方式就是寫一個 script 標籤,然後 src 屬性指向你所寫指令碼的路徑,然後它就可以照常開始工作啦!
但是,你知道這種方法有什麼問題嗎?

問題就在於 JavaScript 會阻塞渲染。

這是什麼意思?
當你的瀏覽器載入頁面的時候,它會轉換 HTML 文件成為標籤,然後構建 DOM 樹。隨後它會使用 DOM 樹渲染頁面。
問題在於,JavaScript 程式碼可以改變 DOM 樹的構建方式。

例如,JavaScript 可以通過 document.write 寫一個 HTML 註釋的起始標籤到文件中,然後整個 DOM 樹都會被毀掉。
這就是為什麼瀏覽器在碰到 script 標籤的時候會停止渲染頁面,這樣做可以防止 document 做多餘的工作。

從瀏覽器的角度來看:
——瀏覽器遍歷文件,然後會解析它
——在某些時刻,瀏覽器遇到了 script 標籤,然後停止了 HTML 轉換,它開始下載並執行那些 script 程式碼
——一旦程式碼執行完畢,瀏覽器繼續遍歷 HTML 文件,然後渲染頁面

實際上,這意味著當你新增一個 script 標籤到頁面中時,它後面的內容在它下載並執行完畢之前都是不可見的。如果你新增一個 script 到 head 標籤中,所有的內容都會變得不可見——直到 script 被下載執行完畢。

那我們該怎麼辦呢?應該使用 async
和 defer
屬性。
這些屬性讓瀏覽器直到 script 指令碼可以在後臺下載,不必阻塞文件渲染,下面是詳細的介紹:
—— async
讓瀏覽器非同步下載(在後臺)script 程式碼,然後繼續解析渲染 HTML。(如果在頁面渲染完畢之前,script 程式碼已經下載好了,那麼就先停止渲染,先執行 script 程式碼。由於下載所消耗的時間通常大於 HTML 轉化,所以這種情況實際上不多見)。
—— defer
會告訴瀏覽器在後臺非同步下載 script 程式碼,直到 HTML 轉化渲染完畢才開始執行這些 script 程式碼。

這裡有兩大不同點:
—— async
script 標籤會在下載之後儘快地執行,它們的執行順序沒有規律。這就意味著有 async 屬性的 React bundle script 和 app bundle script 在同一時刻開始下載,由於 app bundle 更小所以會先下載完畢,導致 app 的 bundle script 先執行。然後網站就崩掉了~
—— defer
不像 async
,會在載入以及文件渲染完畢之後按照 script 標籤的順序開始執行,因此, defer
是更適合的優化方案。

三、程式碼切割

繼續。
很多時候,應用都是打包到一個 bundle 裡面,然後每次請求都發送到客戶端。但是這樣做的問題在於有些頁面我們見到的場景很少,但是它們的程式碼同樣被打包到了我們的 bundle 中,這樣每次頁面載入的程式碼多於實際需要,造成了效能浪費。

這個問題通常使用程式碼切割進行解決,把大的 bundle 切割成一個個小的。
通過程式碼切割,我們把不同功能的程式碼打包到了不同的檔案,只在必要的時候載入必要的程式碼。由於使用這樣的做法,使用者再也不會下載他們不需要用到的程式碼了。

那麼我們怎麼切割程式碼呢?
首先,你需要一個程式碼打包工具,比如 Webpack、Parcel 或者 Rollup。所有的這幾個工具都支援一個特殊函式 import()
。
在瀏覽器中, import()
接受傳遞給它的 JS 檔案並非同步下載該檔案。這可以用於載入應用程式一開始不需要但是接下來可能會用到的庫。

但是在打包工具中, import()
的功能又有所不同。如果你在程式碼中傳遞了一個檔案給 import()
並且在之後進行打包,打包工具會把這個檔案以及其所有的依賴打包到一個單獨的檔案中。app 執行到 import 函式時會單獨下載這個檔案。
因此,在上方的例子中,webpack 會把 ChangeAvatarModal.js
及其依賴打包到單獨檔案中。在程式碼執行到 import 時,這個單獨檔案會被下載。
這就是實際的程式碼切割。

第二,在 React 和 Vuejs 中,會有基於 import()
的工具能夠讓你的程式碼切割工作更加輕鬆。
例如, react-loadable
是一個元件,用於等待其他元件載入,在其他元件載入時,它會進行佔位。React 16.6 添加了一個相似的內建功能元件,叫做 Suspense
。此外 Vuejs 也已經支援非同步元件一段時間了。

如果優化得很好的話,我們可以減少很多不必要的資料的下載,程式碼切割能夠成為最重要的流量優化工具。
如果你的 app 只能做一種優化的話,那就是程式碼切割。
四、移除依賴中的未使用程式碼

另外一個重要的優化點在於包的依賴。
——例如,momentjs 這個庫,用於進行時間操作,它包含了大約 160 kb 大小的不同語言的檔案包。
——React 甚至把 propTypes
包含在生產環境的包中,儘管這不是必要的。
——Lodash,你很有可能引入了整個完整的包,儘管你可能只需要其中的一兩個方法。
上面這些就是把不必要的程式碼引入打包的情況。

為了幫助開發者移除多餘的程式碼,作者和谷歌一起維護了一個 repo 收集關於如何在 webpack 中優化你的依賴,使用這些建議可以讓你的 app 更快更輕巧!
→ GoogleChromeLabs/webpack-libs-optimizations
五、總結

以上都是 JavaScript 的優化方式,總結起來就是:
——壓縮你的 js 程式碼
——使用 async
和 defer
載入 script
——切割你的程式碼,讓應用只加載必須的程式碼
——移除依賴中實際未使用的程式碼
CSS
接下來是如何優化 css 程式碼。

一、壓縮 CSS 程式碼
首先,壓縮 CSS,就像 JavaScript 程式碼一樣。刪除不必要的空格和字母來使你的程式碼更小。

這些工具可以幫助你壓縮 CSS 程式碼:
—— webpack’s postcss-loader
with cssnano
—— PostCSS’s cssnano
—— Gulp’s gulp-clean-css
二、提取 Critical CSS

第二、styles 阻塞渲染,就像之前 script 那樣。

因為沒有樣式的網站看起來很奇怪。
如果瀏覽器在樣式載入之前渲染頁面,那麼使用者就會看到上面那樣的情況。

然後頁面就會閃爍,然後就會看到上面截圖這樣子,很難說是一種好的使用者體驗。

這就是為什麼樣式載入的時候頁面會是空白的。
現在有一種比較機智的優化方式。瀏覽器在載入樣式之前保持空白頁是很有理由的,我們不必從這一點下手。但是我們仍然可以想辦法讓頁面渲染更快——讓頁面只加載渲染初始介面所必要的樣式,剩餘的樣式在之後載入,這些渲染初始介面所必要的樣式稱為“Critical CSS”。
讓我們看看是怎麼做的。

1、把頁面樣式分為 critical 的和 non-critical 的。
2、把 critical CSS 嵌入到 HTML,真能夠讓它們儘快地被載入。

現在,當你載入頁面的時候,頁面能夠很快地被渲染,但是你仍然得載入那些不重要的 CSS。
有多種方式可以載入剩餘的 CSS,下面的方式是我所傾向的:
3、使用 <link rel="preload">
獲取非必要的 CSS。
4、 一旦檔案被載入到快取以後,把 rel
屬性從 preload
切換為 stylesheet
。這可以讓瀏覽器從快取中獲取 CSS 並應用到頁面中 。

那我們怎麼知道哪些 CSS 是必須的,哪些 CSS 是不必須的呢?通常情況下,規則如下:
移除 CSS 樣式知道頁面看起來變得滑稽,那麼剩下的 CSS 就是必要的。 複製程式碼
例如,頁面的佈局樣式或者文章的文字樣式是必須的,因為缺少它們會使得頁面看起來很爛。而 JavaScript 彈出窗或者頁尾的樣式是非必須的,因為使用者不會在一開始就看到它們,缺少那些樣式,頁面看起來仍然十分完美。

聽起來可能比較複雜,但是有很多自動化工具可以幫助我們完成這項工作。
—— styled-components
. It’s a CSS-in-JS library that extracts and returns critical styles during server-side rendering. It works only if you already write styles using styled-components
, but if you do, it works really well.
—— critical
. It’s a utility that takes an HTML page, renders it in a headless browser and extracts styles for above-the-fold content. Because critical
runs only over a single page, it might not work well for complex single-page apps.
—— penthouse
. It’s similar to critical
but works with URLs instead of HTML pages.

這種做法一般可以節約 200 ~ 500 ms 左右的首屏渲染時間。
瞭解更多 Critical CSS 的知識,閱讀 the amazing Smashing Magazine’s guide .
三、總結

這就是 CSS 優化的主要策略,總結起來就是:
——壓縮 CSS 程式碼
——提取必要的 CSS,讓頁面首先載入它們
HTTP
現在讓我們看看 HTTP 的優化。

一、壓縮程式碼
讓 HTTP 傳輸較少資料的方式仍然是壓縮程式碼,本節主要說壓縮 HTML 程式碼,JS、CSS 的程式碼壓縮在之前已經講過了。
二、GZIP 壓縮

壓縮程式碼的第二種方式是 GZIP 壓縮。
Gzip 是一種演算法,它可以使用複雜的歸檔演算法壓縮你傳送到客戶端的資料。在壓縮之後,你的檔案看起來像是無法開啟的二進位制檔案,但是它們的體積會減小 60% 到 80%。瀏覽器接受這些檔案之後會自動進行解壓縮。

基本上,使用 Gzip 已經是生產環境的標準,因此如果你使用一些流行的伺服器軟體比如 Apache 或者 Nginx,你就可以修改配置檔案開啟 Gzip 壓縮。
Apache instructions· Nginx instructions
注意:
使用這些說明啟用 Gzip 將會導致伺服器動態壓縮資源,這會增加伺服器響應時間。在大多數情況下你不需要關心這一點,但如果你希望提高響應時間,可以在構建的時候進行資源預壓縮。

注意:
不要對文字檔案之外的檔案進行 Gzip 壓縮!
影象、字型、視訊或者其他二進位制檔案通常已經被壓縮過了,因此對它們進行 Gzip 壓縮只會延長響應時間。SVG 圖片是唯一的例外,因為它也是文字。
三、Brotli 壓縮

Gzip 有一個替代品,一種叫 Brotli 的演算法。
__Brotli 的優點:__同樣的 CPU 載荷下, 它壓縮效率比 Gzip 高 20% 到 30% 。就是說可以減少 30% 下載量!
__Brotli 的缺點:__它很年輕,瀏覽器以及伺服器的支援度還不夠,所以你不能用它來替代 Gzip。但是可以針對不同的瀏覽器使用 Gzip 或者 Brotli。

Apache 從 2.4.26 開始支援 Brotli,Nginx 有外部模組支援 Brotli。
Apache instructions· Nginx module
注意:
不要把 Brotli 的壓縮等級設定到最大,那樣會讓它壓縮得比 Gzip 慢。設定為 4 是最好的, 可以讓 Brotli 壓縮得比 Gzip 更小更快 。
四、CDN

現在,我們聊聊 CDN。
什麼是 CDN?假設你在美國假設了一個應用。如果你的使用者來自華沙,他們的請求不得不從波蘭發出,一路顛簸來到美國,然後又得回到波蘭。這個請求過程將會消耗很多時間:
——網路請求要跨越很長的一段距離
——網路請求要經過很多路由或者類似裝置(每個裝置都有一段處理時間)
如果使用者想要獲取 app 資料,而且只有美國的伺服器知道如何處理資料,那上面這些過程好像都是必要的。但對於靜態內容而言,上面的請求過程完全沒有必要,因為它們請求的只是一些靜態內容,完全可以部署到任何伺服器上。

CDN 服務就是用來解決這個問題的。CDN 代表“Content Delivery Network(靜態內容分發)”,CDN 服務在全世界提供許多伺服器來 “serve” 靜態檔案。如果要使用的話,只需要在一個 CDN 服務註冊,然後上傳你的靜態檔案,然後更新 app 中引用的檔案的地址,然後每個使用者都會引用離他們最近的伺服器上的靜態檔案了。
根據我們的經驗,CDN 基本上能把每個請求的延遲從上百毫秒減少到 5-10 毫秒。考慮到當頁面開啟時有很多資源要載入,CDN 的優化效果是很驚人的。
五、資源預載入

你知道嗎?谷歌在你開始點選搜尋之前已經在載入搜尋結果的第一項了。這是因為 三分之一的使用者會首先點選第一個搜尋結果 ,預載入內容可以讓使用者更快的看到目標頁面。
如果你確定你的頁面或者資源會在不久之後被用到,瀏覽器允許你進行預載入。

有五種方法可以實現預載入,它們每一種的適用場景都不同:
—— <link rel="dns-prefetch">
提示瀏覽器對一個 IP 地址提前進行 DNS 請求。這對於 CDN 請求很有用,此外一些你知道域名但是不知道具體地址的資源的預載入也可以使用。
—— <link rel="preconnect">
提示瀏覽器提前連線到某臺伺服器。在 dns-prefetch
適用的場景同樣適用,但它可以建立完整的連線並節約很多時間。缺點是開啟新的連線很消耗資源,因此不要過度使用。
—— <link rel="prefetch">
會在後臺對資源進行低優先順序預載入然後快取,這個比較有用,比如在進入 SPA 的下一個頁面之前載入 bundle。
—— <link rel="preload">
會在後臺對資源進行高優先順序的預載入。這對於載入短時間內即將用到的資源而言比較有用。
—— <link rel="prerender">
會在後臺預載入特定頁面,然後在不可見的 tab 中渲染頁面。當用戶進入這個頁面時,頁面可以立馬呈現在使用者面前。這是谷歌用於預載入第一條搜尋結果的方案。
注意:
不要過度使用預載入,雖然預載入能夠提升使用者體驗、加速應用,但是會導致不必要的流量消耗;尤其是在移動端,使用者會消耗過多的不要的流量,這同樣會降低使用者體驗。
閱讀更多: Preload, prefetch and priorities in Chrome · Prefetching, preloading, prebrowsing
六、總結

HTTP 優化方式:
—— 使用Gzipand Brotli 壓縮文字資源
—— 使用 CDN 節省靜態資源的下載時間
—— 預載入一會將要用到的資源
圖片

繼續,說說圖片優化。
一、合適的格式

圖片消耗了大量的流量,但慶幸的是圖片載入不阻塞渲染。但圖片優化仍然是必要的,我們需要讓圖片載入更快、消耗更少的流量。
第一,也是最重要的一點,選擇合適的圖片格式。
最常見的圖片格式是: svg
、 jpg
、 png
、 webp
和 gif
。

svg
最適合向量圖,比如 icon 和 logo。

jpg
最適合照片,因為它壓縮圖片時質量損耗最小,以至於肉眼難以發現。

png
適合沒有任何質量損失的光柵圖形 - 例如光柵圖示或畫素藝術。

webp
最適合照片或者光柵圖片,因為它支援有損或者無失真壓縮。它的壓縮比也比 jpg
和 png
更優秀。
不幸的是 webp
只能在 chrome 使用,但是你仍然可以使用 jpg
和 png
來實現一個 fallback。

上面就是具體實現。
這樣寫的話,支援 webp
的瀏覽器會載入 webp
格式的圖片,不支援 webp
格式的瀏覽器會載入 jpg
最為備用方案。

最後是 gif
。
不要使用 gif
,它非常笨重。超過 1M 的 gif 最好使用視訊檔案代替,可以更好的壓縮內容。
See also: Replace Animated GIFs with Video at WebFundamentals
二、圖片壓縮

除了使用合適的圖片格式以外,圖片壓縮也可以是優化方案。下面是幾種圖片壓縮方式:

首先是 svg
:
——壓縮。因為 svg 圖片是文字,所以可以移除空格和註釋
——簡化 path,如果 svg 是自動工具生成的,其內部的 path 可能會很複雜,這種情況下,移除那些不影響 svg 樣式的 path
——簡化 svg 檔案結構,如果 svg 是自動工具生成的,通常會包含很多多餘的 meta 元素,移除它們可以減小檔案體積
這些優化方式都可以直接使用 svgo
實現,它還有 UI 介面: a great UI for svgo

第二個: jpg
。
——減小圖片維度。根據我的經驗,這是一個開發人員使用 jpg 常犯的錯誤

這種情況常發生於我們把一張大尺寸的圖片塞進一個小尺寸的容器中時。比如我們把一張 2560 * 1440 px 的圖片放到一個 533 * 300 px 的容器中。
當這種情況發生時,瀏覽器會載入過大的檔案,然後還要花時間縮小圖片,知道能夠塞進去那個小小的容器,這些都是無用功。
要解決這個問題,可以直接在你的 PS 或者其他工具中對圖片進行編輯;或者你也可以使用 webpack loader(比如 responsive-loader
)。如果要使用大尺寸圖片適配高分屏,可以通過 <picture>
或者 <img srcset>
代替。

還可以對 jpg 進行圖片降維壓縮,圖片質量壓縮到原來的 70 ~ 80,圖片壓縮導致的質量損失會很難發現。


上面可以看出壓縮後圖片質量損失不大。

但是我們可以看到圖片的大小減小了很多。這就是為什麼推薦對 jpg 圖片進行 70-80 水平的壓縮,因為圖片資訊損失很小,但是體積壓縮很大。

除了以上方式外,我們還可以使用漸進式圖片。

上方是非漸進式圖片載入的方法。

這是一張漸進式的圖片的載入方式。
可以通過 PS 或者 Gimp 製作漸進式圖片。也可以使用 webpack-loader(比如 image-webpack-loader
)或者其他工具。
注意:
漸進式圖片可能比常規圖片更大,而且解碼更慢。

第三, png
。
——使用隔行掃描 PNG。 隔行掃描 PNG 的工作方式與漸進式 JPEG 相同:它從低質量開始渲染,但在載入時進行改進。 但它不是適合所有場景。例如,逐步載入 PNG 圖示看起來很奇怪 - 但它可能適用於其他某些影象。
——使用索引顏色。 通過使用索引顏色,PNG 圖片將其所有顏色放入調色盤中並使用它來引用每種顏色。 這使得每個畫素所需的位元組數更小,並且可能有助於降低整體影象權重。 由於調色盤大小有限(最多256種顏色),因此此解決方案不適用於具有大量顏色的影象。
這兩種方式都可以通過圖片編輯器或者 image-webpack-loader
或者其他工具實現。

以上的所有優化都可以使用自動化工具完成,之前都已經提到過,但是這裡再總結一下:
— webpack has image-webpack-loader
which runs on every build and does pretty much every optimization from above. Its default settings are OK
— For you need to optimize an image once and forever, there’re apps likeImageOptim and sites likeTinyPNG.
— If you can’t plug a webpack loader into your build process, there’re also a number of CDNs and services that host and optimize images for you (e.g.,Akamai,Cloudinary, orimgix).
三、總結

圖片優化總結:
—— 選擇合適的圖片格式
——通過圖片降維、質量壓縮或者使用漸進式圖片優化圖片載入時間
字型

最後一個優化方式就是字型了。
有時候頁面載入好了,所有的樣式、佈局都已經可見了,但是字型還沒有出現或者顯示異常,這就是字型問題所導致的,自定義字型尚未下載完畢,這個時候瀏覽器會等待幾秒,如果仍然未下載,瀏覽器才會使用備用字型作為替代。
這種行為在某種程度上避免了字型的閃爍,但是在緩慢的網路條件下,這種行為使得頁面載入變得緩慢。
一、指定 fallback 字型
我們需要了解一下如何優化這種情況。

首先,要記得設定 fallback 字型。
fallback 字型會在自定義字型無法下載或者下載時間過長時被使用。它在 CSS 的 font
或者 font-family
的第一個指定字型後面指定,比如上方的 Arial, sans-serif
。
fallback 字型應當是比較流行的內建字型(比如 Georgia);也可以是比較通用的字體系列(如 serif 或者 sans-serif);通常情況下,即使你指定了內建的字型作為 fallback,但是你仍然需要新增一個通用的字體系列——因為內建字型可能也會在某些裝置上缺失。

沒有 fallback 字型的話,一旦自定義字型缺失,瀏覽器會使用預設的serif font 進行渲染。這樣可能會導致頁面比較難看。

使用 fallback 字型,至少你有機會定義一個和你的自定義字型相近的字型作為備用方案。
二、使用 font-display

第二點優化,使用 CSS 的 font-display
屬性指定自定義字型。
font-display
屬性會調整自定義字型的應用方式。預設情況下,它會設定為 auto
,在大部分主流瀏覽器中,意味著瀏覽器會等待自定義字型載入 3s。這意味著如果網路太慢的話,使用者需要等待 3s 後字型才會顯示。
這很不好,為了優化這一點,指定 font-display
。
Note: in Microsoft Edge, the font-display: auto
behavior is different. With it, if the custom font is not cached, Edge immediately renders the text in the fallback font and substitutes the custom font later when it’s loaded. This is not a bug because font-display: auto
lets browsers define the loading strategy.

有兩個 font-display
的值我認為比較適用於大部分情況。
第一個是 font-display: fallback
。這樣指定的話,瀏覽器會使用最早能夠獲得的字型立即渲染,不管是已經快取的自定義字型還是 fallback 字型。如果自定義字型沒有被快取的話,瀏覽器會下載它。如果下載得足夠快(通常是 3s 內),瀏覽器會使用自定義字型替換 fallback 字型。
這種情況下,使用者可能會在讀 fallback 字型的文字時,瀏覽器突然進行字型替換,這對於使用者體驗而言並不是很差,總比不顯示任何字型要強。

第二個適用的 font-display
值是 optional
。使用這個值,瀏覽器同樣會立即使用可獲得的字型進行文字渲染:不管是已快取的自定義字型還是 fallback 字型。但是當自定義字型未快取時,在下載好自定義字型後,瀏覽器不會立即替換已有的 fallback 字型,直到頁面下一次重新整理。
這種行為意味著使用者始終只會看到一種字型,不會出現字型替換的情況。

那我們該如何選擇這兩個值呢?
我相信這是一個品味問題。 我個人更喜歡用自定義字型展示文字,因此我選擇 font-display:fallback
值。 如果你覺得訪問者第一次訪問時看到 fallback 字型的頁面沒有什麼關係,那麼 font-display:optional
對您來說非常有用。
Note: this font-display
trick is not applicable to icon fonts. In icon fonts, each icon is usually encoded by a rarely used Unicode character. Using font-display
to render icons with a fallback font will make random characters appear in their place.