Webフォントの非同期読み込み

Webフォントの読み込みは@importだと色々まずいので、主にlink要素を使って並列に読み込むわけだけど、これもまた貴重なHTTPリクエスト数を消費するとか、CSSのパース完了が少し遅れるなどあって、完璧な解というわけじゃない。それを非同期にWebフォント定義の含まれるCSSファイルを読み込むようにして、Webフォントのロードをページのレンダリングと並行して行わせるのはどうか、という試み。

非同期化することによりWebフォント定義の含まれるCSSファイルのリクエストとパースが、ページのレンダリングと並行して行われるようになる。head要素内でlink要素を直接書いた場合は、Webフォント定義の含まれるCSSのリクエストとパース後にページのレンダリングが始まることが多いので、体感速度(ページのレンダリングの開始までの所要時間)は向上する可能性が高い。

動的なlink要素の追加

いわゆるソーシャル・ボタンのやっている動的なscript要素の追加と同じ手法。

(function (d, f) {
  var l = d.createElement('link');
  l.rel = 'stylesheet';
  l.href = f;
  var s = d.getElementsByTagName('script')[0];
  s.parentNode.insertBefore(l, s);
})(document, 'http://example.com/font/foo.css');

script要素の代わりにlink要素を作って突っ込む。置く場所はhead要素の最後がベストだと思う。複数のWebフォントへの対応は要改善。

rel="stylesheet"の後付け

とりあえずWebフォントのURLをlink要素として仕込んでおいて、後でrel="stylesheet"を追加させることにより非同期化する手法。

<link
  href="http://example.com/font/foo.css"
  class="async-web-fonts">
<script>
  (function () {
    var webfonts = document.querySelectorAll('link.async-web-fonts');

    for (var i = 0, l = webfonts.length; i < l; i++) {
      webfonts[i].rel = 'stylesheet';
    }
  })();
</script>

rel="stylesheet"がなければ読みに行かないので、これでも非同期になる。複数のWebフォントのURLを参照するようなケースでも拡張しやすい。見た感じのままDOM操作を最小限に抑えられているけど、総合的なパフォーマンスは動的にlink要素を突っ込むのとさほど変わらなそう。単体のJavaScriptファイルとして切り出せないのが欠点。


ただし、どちらの非同期読み込み手法でもフォントの読み込み中は他のフォントで表示されるので、FOUTを強制するということになる。フォントのロード完了後にすり替える実装をこのウェブサイトで採用していた時に同じ様にFOUTを強制していて、無視できない程度の数の気になる・戻して欲しいといった意見を貰ったので、この非同期読み込みも採用は慎重に行った方が良さそう。

問題の系統としては、ソーシャルボタンのスクリプトを非同期読込させてページの表示までの速度は上がったけど、遅れて実行されるスクリプトのせいでスクロールがつっかかって悪影響みたいなのとちょっと似てる。こういったページの読み込み速度という面からのみの調整だと漏れてしまう事象について、ユーザーテスト以外のテスト手法があると良いとよく考える。