Google webFont 直接引入使用所產生的問題
在外部資源來說,以網頁最需要取得相關的圖片與型字資源後,才能進行後續程式碼的規劃,圖片載入頁面又由其重要,除非頁面是給容器固定的高寬度值,不然也會對頁面上的佔位空間有所影響。
此外字體部份影響上比較沒像圖片會和版面呈現上較直接,可以先透過網頁安全字體進行渲染,直到 webFont 取得資源後在將畫面上的字體做一次更動。以上的部份多少會影響到使用者體驗,透過取得資源的順序方式改善。
快取與取用資源
preload、prefetch 於 HTTP Cache 與 Memory Cache 的快取方式
preload 和 prefetch 取得的資源都會存在 HTTP Cache 中,再放到 Memory Cache,不能快取的話就只會在 memory cache。
preload 和 prefetch 在取得資源前會先查詢快取,若快取沒過期就用快取的檔案不再重抓。
資源優先取用順序
1. Highest: HTML, CSS, Fonts
2. High: script (預載影像之前), 在 viewport 內的影像
3. Medium: script
4. Low: script (async), image, media
5. Lowest: mismatched CSS, prefetch resources
preload、prefetch、preconnect
preload、prefetch、preconnect 標籤的使用概念
以近代主流瀏覽器來說,大約於 2020 年的前後也都有大約有支援 HTML 標籤屬性 rel 的用法,查看到 2021 後的瀏覽器上也大都可使用。
caniuse – Resource Hints: preload
caniuse – Resource Hints: prefetch
preload:此資源對目前的頁面是必要的,請用最快的速度下載此資源。
preconnect:此網頁不久後將來下載某個 domain 的資源,先行建立連線。
prefetch:此資源等等才會用到,有空在下載。
關鍵轉譯路徑最佳化 HTML、CSS、JS,影響使用者體驗
關鍵轉譯路徑用來改善網頁效能,優先顯示與使用者要在網頁上執行的主要操作有關的內容。
收到 HTML、CSS 和 JavaScript 位元組,再對程式碼進行必需的處理,到最後轉變為顯示像素的過程中還有許多中間步驟。將效能最佳化其實就是瞭解這些步驟中所有的活動,這就是所謂的關鍵轉譯路徑。
Optimized (優化) 與 Unoptimized (未優化) 的畫面渲染處理方式。
preload:開啟頁面優先取連結資源
preload:此資源對目前的頁面是必要的,請用最快的速度下載此資源。
CSS 與 JS 的使用
rel="preload" 標籤,以 as 指定 script 或是 style 資源類型。
1 2 | <link rel="preload" as="script" href="xxx.js"> <link rel="preload" as="style" href="xxx.css"> |
script 一般放在 html 的最後面,等到 parse 完整個 html 被執行。
重要的資源以 preload 是比較好的優化方式。相較之下使用非同步下載資源 async script 較會會產生出 block onload event (阻止加載事件)。
Font 的使用
rel="preload" 標籤,以 as 指定 font 資源類型。
crossorigin="anonymous" (跨域屬性設定) 列為需要加的屬性,以 anonymous mode CORS 方式取得資源,否則字體會被重複下載兩次。
1 | <link rel="preload" as="font" crossorigin="anonymous" type="font/woff2" href="xxx.woff2"> |
HTML5 部份元素有 CORS 的支援,可見 HTML5標簽的crossorigin屬性,提供元素CORS跨域設定 一文。
preconnect:將連結的 domain 資源先建立好連線
preconnect:此網頁不久後將來下載某個 domain 的資源,先行建立連線。
1 | <link rel="preconnect" href="https://xxx.com"> |
提前建立連線,瀏覽器在實際傳輸資源前有幾個需要的步驟。
1. 向 DNS 請求解析域名
2. TCP Handshake (TCP 交握)
3. SSL Negotiation (HTTPS 連線)
4. 連線建立完成,等待拿到資料的第一個 byte
以 preconnect 的使用上,基本上可應用於 CDN 或是 Streaming (串流媒體) 上,在切換頁面前可以先進行取得資源進入到快取,當連結下一頁時結省下時間開啟頁面所建立連結的時間。
prefetch:等下用到的資源待必較資源取得後接續取
prefetch:此資源等等才會用到,有空在下載。
prefetch 主要是用在同頁資源時預先載入,資源等頁面完全下載完以後以 Lowest (最低) 優先度下載。
在同頁用 JS 的 AJAX 行為抽換局部內容,若資源檔過大,排於 preload 優先必取資源先渲染主要頁面後,之後以 prefetch 接著於背後取得後續資源,在處理畫面抽換區塊內容,可達到較好的呈現 (例如:抽換高解析圖檔抽換區塊會漸漸呈現圖片)。
preload 實作 Google webFont
以下以 Noto Sans Traditional Chinese 使用 link 方式戴入字體資源。
1 2 3 | <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+TC:wght@900&display=swap" rel="stylesheet"> |
preconnect 於主要資源取得後,再取 Google webFont (官方預設產生連結方式)
直接以 Google webFont 官方引入方式,透過 <link rel="preconnect"> 方式引入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <style> @import url('https://fonts.googleapis.com/css2?family=Noto+Serif+TC:wght@900&display=swap'); .font--NotoSerifTC { font-family: 'Noto Serif TC', serif; } body { font-size: 25px; } </style> </head> <body> <p class="font--NotoSerifTC"> 人皆生而自由;在尊嚴及權利上均各平等。人各賦有理性良知,誠應和睦相處,情同手足。 人皆生而自由;在尊嚴及權利上均各平等。 </p> </body> </html> |
引入結果查看到開發者工具,會發現因為 <link rel="preconnect"> 取用字體資源,會於相關資源都引入後,在頁面結構產生時才進行取得字體,所以相關字體會是在後方。
小結:這樣引用字體資源的方式,有個好處是可配合上瀏覽器安全字體先處理畫面,待 webFont 資源取得後再進行重新更換顯示字體。
改用 preload 將字體於頁面優先取得
以思源繁中字體,引入的資源檔相當多,詳見 https://fonts.googleapis.com/css2?family=Noto+Serif+TC:wght@900&display=swap,每個 @font-face {} 中 src: url() 就是對應的字體資源。
針對 src: url() 相關的字體資源,直接置於 HTML head 結構中的 <link>,加入屬性 rel="preload" 與 as="font",並開啟頁面透過開發者工具查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <!-- 使用 css2 連結文件中的字型檔,以 preload 預先載入 --> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.119.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.114.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.118.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.116.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.112.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.115.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.117.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.107.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.113.woff2" as="font" crossorigin /> <link rel="preload" href="https://fonts.gstatic.com/s/notoseriftc/v17/XLY9IZb5bJNDGYxLBibeHZ0BvvMpbXxGSMoPW2CYaL4xcgZt2hLi5AU2hsKUwIdeS7qKC8bpy_5IYlDy.50.woff2" as="font" crossorigin /> <style> @import url('https://fonts.googleapis.com/css2?family=Noto+Serif+TC:wght@900&display=swap'); .font--NotoSerifTC { font-family: 'Noto Serif TC', serif; } body { font-size: 25px; } </style> </head> <body> <p class="font--NotoSerifTC"> 人皆生而自由;在尊嚴及權利上均各平等。人各賦有理性良知,誠應和睦相處,情同手足。 人皆生而自由;在尊嚴及權利上均各平等。 </p> </body> </html> |
引入結果查看到開發者工具,會發現到相關的字體資源都提於前面,相關資源都完整取得後,才會透過渲染畫面。
小結:在取得資源時,因為是主要優先取得,如果裝置的網速不快,會產生過久的空白畫面。
JS 要規劃優先取得資源後才渲染畫面,另可使用 Web Font Loader (字體載入事件) 的 JS 插件,套件主要目的就是以 Javascript 的方式載入Web Font,並透過傳遞一些參數,來改變載入 Web Font 行為,待取得資源與字體載入完成後,才進行畫面渲染。
Web Font Loader 網頁字體裝載機 (繁中文件說明)