徒然技術日記

Object.prototype.__noSuchMethod__

web.dev の Fast load times を読んだ

パフォーマンス改善のための基礎知識をつけるべく,Chrome Dev Summit 2018 で紹介された web.dev に書かれているドキュメントのうちパフォーマンスに関するもの

web.dev

を一通り読んだので,簡単なまとめとして読書メモを書いた.

デブサミには幸運にも会社から派遣メンバーとして選ばれて参加したが,全体的にパフォーマンスに関する話題が多く,Google としてもパフォーマンス向上に力を入れていることが強いメッセージとして伝わってきた. web.dev はその啓蒙活動の一環としてドキュメントと Lighthouse の Web 版が提供されている.

Discover performance opportunities with Lighthouse

  • lighthouse は metrics (現在のページのパフォーマンスの状態を表す指標) と opportunities (パフォーマンスの改善ポイント) を提供する
  • web.dev または Chrome の DevTools から実行できる
  • スコアについての詳細は https://developers.google.com/web/tools/lighthouse/v3/scoring に書かれている
  • https://web.dev/measure から Profile にサイトを登録することで, daily に Lighthouse のレポートを測定してくれるようになる

Use Imagemin to compress images

  • imagemin で適切に画像を圧縮する話
  • ビルド時に毎回圧縮するのは無駄が多いので,画像追加時に一回だけやりたい
    • PR 時に適切に圧縮されているかどうか調べたい
    • GitHub Actions でチェックできるかも?

Replace animated GIFs with video for faster page loads

  • ffmpeg で gif を webm/mp4 に変換して video タグに置き換えればファイルサイズがずっと小さくなる(例では1/10になっている)

Use lazysizes to lazyload images

  • 画像を lazyload することについて
  • Intersection Observer の活用を頑張らなければ...

Serve Responsive Images

  • viewport の大きさごとに最適なサイズの画像を配信することについて
  • sharpImageMagick を使うことで画像のリサイズが可能
  • img 要素の srcset 属性で width descriptors を使うことでブラウザに画像サイズを伝えることができる
    • ブラウザ側が最適な大きさの画像を取得してくれる
    • バイスの解像度に応じて画像を変える density descriptors もある
  • width descriptors を使う場合は sizes 属性も同時に使う必要がある
    • 画像が表示される大きさをブラウザに伝える
  • picture 要素を使うとより細かい制御が行える

Serve images with correct dimensions

  • 適切な画像サイズをどう判断したら良いかについて
  • まずは不適切なサイズの画像を探す: Lighthouse の "Properly size images" audit で調べられる
  • Good approach
    • 絶対単位で大きさが指定されている場合
      • 表示されるサイズにリサイズする
    • 相対単位で大きさが指定されている場合
      • どのデバイスでも適切に表示されるサイズにリサイズする.デバイスのスクリーンサイズは GA などから判断できる
  • Better approach
    • 絶対単位で大きさが指定されている場合
      • srcsetsizes 属性を使ってデバイスの解像度に応じた適切な画像が配信されるようにする
        • おそらく density descriptors のことを指している
    • 相対単位で大きさが指定されている場合
      • srcsetsizes 属性を使ってスクリーンサイズに応じた適切な画像が配信されるようにする
        • おそらく width descriptors のことを指している

Use WebP images

  • webp は 25-35% ファイルサイズが小さく,YouTube ではページ読み込みが 10% 早くなった
  • cwebp または Imagemin で生成可能
  • picture 要素を使って配置する
  • Lighthouse の "Serve images in next-gen formats" audit で調査可能

Apply instant loading with the PRPL pattern

  • critical なリソースを preload する
    • <link rel=preload>
    • リソースのリクエスト優先度をあげることができる
  • First Paint をできる限り早く render する
  • Service Worker を用いてアセットを pre-cache する
  • 必要ないリソースは lazyload する
    • Code splitting することで JS を lazyload できる
      • 重要度の高い chunk は preload の設定をする
    • 画像も lazyload できる

Preload critical assets to improve loading speed

  • <link rel=preload> で優先度を上げられる
    • ブラウザがリソースを発見するのに時間がかかるものの first paint に重要なリソースに対して使うと効果が高い
      • 例: @font-face に指定されるリソースは CSS のパースが終わらないとブラウザが認識できない
    • webpack は /* webpackPreload: true */ を使うと preload を挿入できる
  • <link rel=prefetch>: 現在のページのリクエストが全て完了した後に,次のページ遷移を高速化するための準備を定義できる
    • /* webpackPrefetch: true */ もある

Reduce JavaScript payloads with code-splitting

  • Code-splitting について
  • route もしくは component レベルで splitting するのがシンプルな方法

Remove unused code

  • Lighthouse の "Unused JavaScript" audit で実行されなかったコードを解析できる
  • webpack-bundle-analyzer でバンドルに含まれるモジュールを可視化できる
  • import する対象を限定したり,ライブラリ自体を使わないようにすることでコード量を減らせる

Minify and compress network payloads

  • Minification
    • UglifyJS
  • Data compression
    • gzip, brotli
    • 大抵 CDN がやってくれることが多いはず
    • Dynamic compression
      • リクエストされたタイミングで圧縮する
        • シンプルだがレスポンスを返すのに余計な時間がかかる
    • Static compression
      • 事前に圧縮しておく
        • BrotliWebpackPlugin, CompressionPlugin などの webpack plugin がある

Serve modern code to modern browsers for faster page loads

  • @babel/preset-env
  • <script type=module>
    • preset-env で targets: esmodules: true を設定すると ESM が使えるブラウザのみをターゲットにできる
    • type=module をセットすれば ESM を解釈できるブラウザのみを対象にファイルを配信できる

Avoid invisible text during font loading

  • "flash of invisible text" の代わりに "flash of unstyled text" を目指す
  • font-display: swap; を使う
    • 対応ブラウザが限られる
  • フォントファイルが読み込まれるまで system font を適用しておき,読み込まれたことをトリガーにして custom font を当てるようにする

Using the Chrome UX Report to look at performance in the field

  • CrUX はオプトインしたユーザーから収集した実際の First Contentful Paint (FCP), DOM Content Loaded (DCL), First Input Delay (FID) のデータセット
  • CrUX Dashboard, PageSpeed Insights, BigQuery の3つ手段でデータにアクセスできる
  • CrUX Dashboard
    • Data Studio を使ったパフォーマンスの時間変化を可視化できるツール
  • PageSpeed Insights
    • URL を指定すると直近30日間のデータを集約して表示する
    • API もある
  • BigQuery
    • 自前でいろいろ分析したい時に

Using the CrUX Dashboard on Data Studio

  • Data Studio の Community Connectors を基に作られているツール
  • https://g.co/chromeuxdash にページの origin URL を入れれば dashboard を作成できる
  • dashboard は FCP, デバイス分布,接続状況分布から構成されている
  • FCP
    • Fast (<1s), Average (1~2.5s), Slow (>2.5s) の比率が表示される
  • バイス分布
  • 接続状況分布
    • 4G, 3G, 2G, Slow 2G, Offline の比率が表示される
  • これ以外のメトリクスや国ごとのデータなどを見たい場合は BigQuery を使う必要がある

Using the Chrome UX Report on PageSpeed Insights

  • PageSpeed Insights は Lighthouse と CrUX の結果を一度に見られるようにしたもの
  • Lab data (Lighthouse の結果) と Field data (CrUX のデータ) に分かれて表示される
  • Field data には FCP と FID が Fast, Average, Slow に分けて表示される
    • FCP: Fast (<1s), Average (1-2.5s), Slow (>2.5s)
    • FID: Fast (<50ms), Average (50-250ms), Slow (>250ms)
    • 入力したページのデータの他に, origin の結果(そのページが属する origin の全てのページのデータを集約したもの)も表示される
    • PSI のデータは過去30日のもの.これは月単位でデータを集約する BigQuery とは集計の仕方が異なっているので注意する必要がある
  • PSI は以下の利点がある
    • ページ単位でのパフォーマンスが見られる唯一のツール
    • 日単位でデータを集計してくれる唯一のツール
    • API がある
  • その代わり過去のデータは見られないし,メトリクスも FCP と FID しかない

Using the Chrome UX Report on BigQuery

  • SQL を使って生データを解析できる
  • 国ごとのデータ,月ごとのデータがテーブルとして提供されている
  • 無料の範囲は1ヶ月あたり 1TB で,それ以降は $5/TB の料金がかかる

Performance budgets 101

  • performance budgets
    • パフォーマンスに影響するメトリクスに対する制限
    • 設定した制限が,機能追加・デザイン・技術選定・ライブラリ選定を行う際の判断基準の一つになる
  • 定量的なメトリクス
    • 開発の初期段階で有効
      • サイズが大きくなるのを防ぐことができる
    • 画像サイズ,Web フォントの数,JS のサイズ,外部リソースの数, etc.
    • これらはユーザーエクスペリエンスを直接表していないので注意する必要がある
      • 例えばサイズが小さくても critical path のスクリプトの読み込み優先度が低かったらユーザーになかなか内容が表示されなくなってしまう
  • Milestone timings
  • ルールベースのメトリクス
    • Lighthouse, WebPageTest が提供するスコア
  • どうやって目標値を決定するのか?
    • まずは計測してみる.競合のサイトのスコアも計測してみよう.
    • もしそうした調査にかける時間がない場合には,TTI 5秒以下,critical path のリソースが 170KB 以下というのが一つの目安
      • これは 3G の通信環境を考慮に入れて考えられた数字
  • performance budget の値はページごとに異なりうる
  • 使えるツール
  • 継続的に数値を追うのがよい

Your first performance budget

Performance Budget の決め方について

  1. 最も重要なページがどこかを決める
  2. そのページのメトリクスを Lighthouse で測定する
  3. 競合のメトリクスを調査する
  4. 10サイトくらいは調査した方がいい
  5. milestone timings の budget を決める
  6. 20% ルールが有効
    • レスポンスタイムに 20% の違いがあるとユーザーが気がつくという研究に基づく
  7. initial はまず現在の値から 20% 改善を目指し,最終的には競合より 20% よい値を目指す
  8. 他の種類の budget を決める
  9. 定量的メトリクス
    • 一般的に critical path のリソースは 170KB 以下に保つべき
      • 低スペックで 3G な環境でも快適になる
      • 4G をターゲットにするならもう少し余裕ができる(表を参照
    • コンテンツの種類によっても変わってくるので,最終的な値はその辺りを考慮して決める必要がある
      • e-commerce で画像が多いなら JS の制限をきつくするなど
  10. ルールベースのメトリクス
    • Lighthouse で 85 点以上など
  11. 優先度を考える
  12. ニュースサイトであれば FCP, 検索サイトであれば TTI など重要なメトリクスはサイトによって異なる
  13. CrUX で競合サイトの状況を見てみるのもよい

Incorporate performance budgets into your build process

Performance Budget をビルドプロセスに組み込む方法について

  • Webpack Performance Hints
    • 非圧縮のサイズなのに注意
    • ただし圧縮は転送速度を早めるだけで,特にモバイルで顕著に影響する parse の速度を早めることはないので,非圧縮のサイズを気にすることも大事
  • Bundlesize
    • CLI で使えるほか,CI に組み込める
    • gzip, brotli, none から圧縮方法を選択して,そのサイズに対する budget を設定できる
  • Lighthouse Bot
    • Travis でしか使えないが,budget に違反している PR を block できる

Using bundlesize with Travis CI

  • Travis で bundlesize を使う方法
    • このページは正直 bundlesize の ReadMe を見れば充分 😗

Using Lighthouse Bot to set a performance budget

  • Lighthouse Bot を使う方法
    • localhost ではなくどこかのサーバーに deploy した方がリアルなデータが得られる