[CSS / Javascript][前端效能調校] Web Font 的非同步下載

Turning Performance CSS Web Font

前言

最近工作上遇到一個很雷的CSS Web Font 引用寫法

引用的是Google的Web Font

CSS代碼如下

/*
* Noto Sans TC (Chinese_traditional) http://www.google.com/fonts/earlyaccess
 */ 
 @font-face {
    font-family: 'Noto Sans TC';
    font-style: normal;
    font-weight: 100; 
     src: url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Thin.woff2) format('woff2'), 
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Thin.woff) format('woff'), 
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Thin.otf) format('opentype');  
} 

/*預設font-weight*/
 @font-face {
    font-family: 'Noto Sans TC';
    font-style: normal;
    font-weight: 400;  
      src: url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Regular.woff2) format('woff2'),
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Regular.woff) format('woff'),
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Regular.otf) format('opentype');  
} 

 

 @font-face {
    font-family: 'Noto Sans TC';
    font-style: normal;
    font-weight: 700; 
      src: url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Bold.woff2) format('woff2'), 
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Bold.woff) format('woff'), 
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Bold.otf) format('opentype'); 
} 

 @font-face {
    font-family: 'Noto Sans TC';
    font-style: normal;
    font-weight: 900; 
    src: url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Black.woff2) format('woff2'), 
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Black.woff) format('woff'),
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Black.otf) format('opentype'); 
} 

/*--------------------------------------------------------------------------------------*/
html, body {
    width: 100%;
    height: 100%;
    font-family: 'Noto Sans TC', "新細明體";
    /*如果HTML元素有未套用到字體的話 ↓*/
    /*font-weight: 400;*/ /*不指定font-weight,預設font-weight:400,所以下載並使用 NotoSansTC-Regular.woff2 */
}

.div1 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 100; /*下載並使用 NotoSansTC-Thin.woff2*/
}
.div2 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 700; /*下載並使用 NotoSansTC-Bold.woff2 */
}
.div3 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 900; /*下載並使用 NotoSansTC-Black.woff2*/
}

以下是我測試用的HTML頁面,畫面很乾淨,只有文字

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Font Demo</title>
    <link rel="icon" href="Content/img/favicon.ico" /> 
     <!--引用剛剛的CSS檔-->
    <link href="Content/css/MyStyle.css" rel="stylesheet" />
</head>
<body>
    <header>我是header</header>
    <hr />
    <div class="div1">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
        ...(略
    </div>
    <div class="div2">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
        <p>
            香港01等港媒報導,港民消費氣氛疲弱,加上訪港旅客數量持續下降,身兼港府行政會議成員的立法會飲食界議員張宇人,
            昨出席電台節目時表示,現時餐飲業市況嚴峻,生意甚至差過2003年SARS時期,暫時有200至300間餐廳結結束營業,憂慮會有倒閉潮出現。
        </p>
        ...(略
    </div>
    <div class="div3">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
        <p>
            香港01等港媒報導,港民消費氣氛疲弱,加上訪港旅客數量持續下降,身兼港府行政會議成員的立法會飲食界議員張宇人,
            昨出席電台節目時表示,現時餐飲業市況嚴峻,生意甚至差過2003年SARS時期,暫時有200至300間餐廳結結束營業,憂慮會有倒閉潮出現。
        </p>
        <p>張宇人透露,目前僅有一、兩間大中型企業肯整體減租,其餘業主仍然考慮中,希望業主可以「一刀切」減租;他亦期望政府可作全數貸款擔保,助業界支付員工薪資。</p>
        <p>對於部分港民發起「黃藍消費戰」活動,他說,對市民因政治理念選擇光顧某些餐廳的做法沒有意見,但是示威者不應破壞某些集團旗下店鋪,令員工受影響。</p>
        ...(略
    </div> 
</body>
</html>

執行結果如下

效能瓶頸分析

上述字型檔,經過我的追查,應該出自此頁:https://fonts.google.com/specimen/Noto+Sans+TC?selection.family=Noto+Sans+TC

把字型檔下載到本機端查看,一個約5MB (汗顏

而且此份CSS檔案,唯一有做到提昇效能的只有 format() 這個寫法Orz

有加format()讓瀏覽器直接判斷,有支援第一順位的.woff2字型才下載來使用,否則略過,從下一個 format('.woff')再去判斷

↓三個url 如果都沒加上format()的話,瀏覽器就會先下載字型檔後,再判斷不支援才繼續找下一個備用字型檔

   src: url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Regular.woff2) format('woff2'),
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Regular.woff) format('woff'),
        url(//fonts.gstatic.com/ea/notosanstc/v1/NotoSansTC-Regular.otf) format('opentype');  

另一個效能瓶頸是 @font-face 的 src 沒加上 local() 先找看看使用者電腦有無安裝該字型,有的話,就不用從網路下載字型檔

而且 @font-face 也沒有加上 font-display:swap; (CSS3屬性 font-display:swap 說明請見其他網友文章:[教學] CSS3新屬性font-display優化雲端字型的載入方式)

所以在網頁一開始載入時,畫面全白,文字沒顯示出來↓ 

經過我嘗試,雖然此份CSS檔案的@font-face 補上 font-display:swap; 後,是可以讓使用者一開始就看到文字(新細明體),不至於畫面全白

但使用者仍然需要等待約2秒鐘之後,才能看到Web Font的字體效果,因為每一個.woff2檔案很大約2MB,不知道當初的網頁設計者怎麼會使用這麼大的檔案

原本我很天真地想找辦法壓縮Minify一下字型檔,我以為就像 .js和.css 一樣可以壓縮&縮小檔案大小

但後來發現… woff 和.woff2本身就已經是壓縮格式了= =",請見:Web開放字型格式- 維基百科,自由的百科全書 - Wikipedia

於是我腦筋動到,嘗試看看能不能以非同步方式載入Web Font

於是找到一個 Google 的 Javascript 套件 Web Font Loader:https://github.com/typekit/webfontloader

一般使用Web Font都是靠CSS載入,這套件主要目的就是以 Javascript 的方式載入Web Font,並透過傳遞一些參數,來改變載入Web Font行為

使用方法

先去它的Github Releases頁:https://github.com/typekit/webfontloader/releases

下載最新版的.zip,解壓後把根目錄下的 webfontloader.js 檔案放到自己的工作專案上,網頁再去引用這支.js

或你的Javascript想直接引用它的CDN也是可以 https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js (但我覺得把檔案放回本機端,效能會比較好)

以下是修改&校能調校過後的完整程式碼

CSS檔↓   (MyStyle.css)

/*--------------------------------------------------------------------------------------*/
html, body {
    width: 100%;
    height: 100%;
    font-family: 'Noto Sans TC', "新細明體";
    /*如果HTML元素有未套用到字體的話 ↓*/
    /*font-weight: 400;*/ /*不指定font-weight,預設font-weight:400,所以下載並使用 NotoSansTC-Regular.woff2 */
}

.div1 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 100; /*下載並使用 NotoSansTC-Thin.woff2*/
}
.div2 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 700; /*下載並使用 NotoSansTC-Bold.woff2 */
}
.div3 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 900; /*下載並使用 NotoSansTC-Black.woff2*/
}

↑ CSS檔案中,@font-face裡的src一定要移除掉,避免瀏覽器發出Request去下載那個龐大的字型檔,或像上述範例一樣,全部 @font-size定義都移除光光讓CSS檔案縮小一點

至於被移除掉的@font-face定義,待會要靠Web Font Loader的JS,動態載入

HTML完整代碼↓

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Font Demo</title>
    <link rel="icon" href="Content/img/favicon.ico" /> 
    <link href="Content/css/MyStyle.css" rel="stylesheet" />
</head>
<body>
    <header>我是header</header>
    <hr />
    <div class="div1">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
         ...(略
    </div>
    <div class="div2">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
          ...(略
    </div>
    <div class="div3">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
         ...(略
    </div>

    <script>
        WebFontConfig = {
            google: { families: ['Noto Sans TC:100,400,700,900&display=swap'] }//記得加上display=swap避免網頁一開始文字消失了~
        };

        (function (d) {
            var wf = d.createElement('script'), s = d.scripts[0];
            wf.async = true;//非同步方式載入.js
            wf.src = 'Content/js/webfontloader.js';

            s.parentNode.insertBefore(wf, s);

        })(document);
    </script>
</body>
</html>

執行結果↓

根據我反覆測試,使用 Web Font Loader 這個套件動態載入字型檔,比起原本寫法,速度可以快約 0.5s ~ 1s

為什麼速度變快了?

用Chrome開發者工具查看使用Web Font Loader套件後,最後生成的HTML網頁結構如下

可以再進一步查看 Web Font字型檔所在的CSS連結:https://fonts.googleapis.com/css?family=Noto+Sans+TC:100,400,700,900&display=swap

↑原本一個將近2MB的字型檔.woff2,依各別語系被拆開細分好幾個大小才幾KB的.woff2

所以瀏覽器才會出現如下,發出這麼多Request去下載字型檔

這時候你可能會有個疑問,瀏覽器同時間發出這麼多Request,不會造成後面的資源下載阻塞嗎

嗯…是有可能XD (逃

好險都才50KB左右,以本範例來說,至少改善了原本Web Font載入龐大、使用者等待時間約2秒的效能瓶頸問題

如果我有發現更好的寫法 或 更好的Web Font載入方式,有空再補充上來~

2019-10-27 補充

後來我反覆嘗試結果,確定原先舊有寫法的效能瓶頸真的是字型檔案太大所造成(接近2MB)

如果不想以Javascript非同步下載Web Font的話,也是可以直接使用Google Fonts API即可,和Web Font Loader套件相比速度貌似快了一些,可能因為少了引用.JS檔(Web Font Loader)吧

完整代碼如下

/*--------------------------------------------------------------------------------------*/
html, body {
    width: 100%;
    height: 100%;
    font-family: 'Noto Sans TC', "新細明體";
    /*如果HTML元素有未套用到字體的話 ↓*/
    /*font-weight: 400;*/ /*不指定font-weight,預設font-weight:400,所以下載並使用 NotoSansTC-Regular.woff2 */
}

.div1 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 100; /*下載並使用 NotoSansTC-Thin.woff2*/
}
.div2 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 700; /*下載並使用 NotoSansTC-Bold.woff2 */
}
.div3 {
    font-family: 'Noto Sans TC', "新細明體";
    font-weight: 900; /*下載並使用 NotoSansTC-Black.woff2*/
}

至於HTML程式碼,根據這兩篇文章說明:

Making Google Fonts Faster⚡

[教學] 運用這三種 Resource Hint 技巧,加快網頁載入的速度:preload,preconnect 與 prefetch By shubo

可以在引用Google Fonts API前先加入"preconnect"預先和fonts.gstatic.com建立連線,待之後Google Fonts API 從 fonts.gstatic.com下載字體時速度會稍微快一些

<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>

完整HTML↓

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Font Demo</title>
    <link rel="icon" href="Content/img/favicon.ico" />
    <!--預先和fonts.gstatic.com建立連線-->
    <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin> 
    <!--直接引用Google Fonts API-->
    <link href="https://fonts.googleapis.com/css?family=Noto+Sans+TC:100,400,700,900&display=swap" rel="stylesheet">
    <link href="Content/css/MyStyle.css" rel="stylesheet" />
</head>
<body>
    <header>我是header</header>
    <hr />
    <div class="div1">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
        ...(略
    </div>
    <div class="div2">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
        ...(略
    </div>
    <div class="div3">
        <p>旺報【記者李文輝╱綜合報導】</p>
        <p>
            受到反送中衝擊,香港餐廳生意比2003年嚴重急性呼吸道症候群(SARS)爆發時更差,代表餐飲界的香港立法會議員張宇人指出,已有超過200家餐廳食堂受影響而結束營業,預料倒閉潮還會持續。此外,訪港客量急跌一半,香港旅遊促進會總幹事崔定邦促請港府當局,提供資金協助交租,並減免機場稅以增加港人出境意願。
        </p>
         ...(略
    </div>
     
</body>
</html>

使用Google Fonts API 來引入字型檔,本身會依各語系把原本好幾MB的字型檔拆開細分讓瀏覽器下載,加快前端網頁的載入速度

至於Google Fonts API的使用方法如下

進入此頁:https://fonts.google.com/

以本文範例,搜尋「Noto Sans TC」之後點擊進去

Google Fonts API 詳細用法可以參考官網:Get Started with the Google Fonts API

 

本文章參考以下資源

How to load web fonts to avoid performance issues and speed up page loading

Load Google Fonts Asynchronously For Page Speed

Case Study - Analyzing Web Font Performance

Optimising Google Fonts for Fast Load Times

`font-display` for the Masses

4 Tips To Faster Loading Google Fonts

Avoid performance loss through webfonts 

 

CSS 網頁字型 @font-face 使用教學與範例