Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実践Webフロントパフォーマンスチューニング

 実践Webフロントパフォーマンスチューニング

2025年度 traP新歓オリエンテーションのLTで発表した内容です。5分のLTなので1章のみを話しました。

しーぴー

April 28, 2025
Tweet

More Decks by しーぴー

Other Decks in Programming

Transcript

  1. 1. 画像を軽くする ー 画像を圧縮する - 画像の情報 →各ピクセルの色の情報 (を集めたもの) - 画像の容量

    → 色の情報 × ピクセル数 (画素数) 268px 268px 1pxあたりの情報量 = 4byte RGBAでそれぞれ0~255の値を取るとしたとき
  2. 1. 画像を軽くする ー 画像を圧縮する - 画像の情報 →各ピクセルの色の情報 (を集めたもの) - 画像の容量

    → 色の情報 × ピクセル数 (画素数) 268px 268px 1pxあたりの情報量 = 4byte RGBAでそれぞれ0~255の値を取るとしたとき 4×268×268=287296B≒280KB
  3. - 色々な画像圧縮アルゴリズム - PNG (可逆圧縮、透明度付き、デジタルイラストなどに効果的) - JPEG (非可逆圧縮、圧縮率を調整可能、写真などに効果的) - GIF

    (可逆圧縮、厳しい色制限、アニメーション可能) - HEIC (主に非可逆圧縮、Apple製品でよく使われる、Webでは非対応) - WEBP (可逆・非可逆圧縮、アニメーション可能、高圧縮率) - AVIF (可逆・非可逆圧縮、高圧縮率、処理が重い) 1. 画像を軽くする ー 画像を圧縮する
  4. 1. 画像を軽くする ー 画像を圧縮する PNG 62.1KB GIF 26.3KB JPEG (q=95)

    20.7KB WEBP (q=95) 13.5KB AVIF (q=95) 14.9KB ※この画像の例であり、画像によってはこの順位が入れ替わります
  5. 1. 画像を軽くする ー 画像を圧縮する - quality を下げても圧縮できる (ただし劣化する) q=100 (可逆)

    17.1KB q=80 7.5KB q=60 5.8KB q=40 4.8KB q=20 3.6KB 同じ画像を quality を変えながら WEBP で圧縮してみた例
  6. 1. 画像を軽くする ー 画像を圧縮する - 具体的には sharp や ImageMagick を用いて変換する

    - quality による劣化の程度は Squoosh などで確認できる $ npx sharp-cli --format webp --quality 95 -i input.png -o output.webp sharp-cli を用いて画像を変換する例
  7. 1. 画像を軽くする ー 画像を圧縮する Q. WEBP/AVIF 未サポートユーザーはどうするの? A. だいたい無視しても良い (ほぼ全ての環境で使える)

    ※サポートしたい場合は Accept ヘッダーを見てサーバー側で画像を出し分ける処理が必要になります
  8. 1. 画像を軽くする ー 画像を小さくする - リサイズも sharp / ImageMagick でできます

    $ npx sharp-cli -i input.png -o output.png resize 350 350 sharp-cli を用いて画像をリサイズする例
  9. 2. テキストを圧縮する - テキスト = HTML/CSS/JS など - 意味のない空白を削る -

    プログラムの意味を変えずに短くする - 一般に minify と呼ばれる作業 - テキストを可逆圧縮する - gzip / deflate / brotli / zstd などの圧縮形式がある
  10. 2. テキストを圧縮する - 現代的なツールを使えば minify オプションはきっとある - ESBuild、Rollup、Parcelなどのバンドルツールはサポートしている - そうでない場合は

    html-minifier などを使おう - テキストの圧縮はミドルウェアでだいたいできる - だいたいのWebサーバー (リバースプロキシ) は圧縮をサポートしてる - 「(自分の使ってるサーバーライブラリ) text compression」で検索
  11. 1. 画像を軽くする ー 画像を圧縮する Q. brotli 未対応ユーザーはどうするの? A. だいたい無視しても良い (ほぼ全ての環境で使える)

    ※サポートしたい場合は Accept-Encoding ヘッダーを見てサーバー側で圧縮方式を変える必要があります ※そして多くのミドルウェアはその機能をサポートしているはずです
  12. 3. フォントを削る - Webフォントは雑に使うと重い (デカい) - 2~3MBは普通に突破しがち - ウェブセーフフォントを使うことも視野に -

    ウェブセーフフォント → デバイスに入っていてダウンロード不要 - どうしてもWebフォントを使う場合はサブセット化する - サブセット化 = 使う文字 (例えば日本語のみ) だけを取り出すこと
  13. 3. フォントを削る - Variable Fonts vs Normal Fonts - Variable

    Fonts = Font Weight (文字の太さ) が自由に変えられるフォント - 様々な Weight を使い分ける場合は Normal Fonts をパターン数使うより は軽くなる場合がある - しかしサイト内で使う Weight のパターン数を減らすことを Variable Fonts を使う前に検討するべき
  14. 4. JavaScriptを削る - JavaScript 特有の圧縮 → mangling function calculateSum(a, b)

    { const result = a + b; return result; } console.log(calculateSum(5, 10)); function a(b, c) { const d = b + c; return d; } console.log(a(5, 10)); 意味は等価
  15. 4. JavaScriptを削る - JavaScript 特有の圧縮 → mangling function calculateSum(a, b)

    { const result = a + b; return result; } console.log(calculateSum(5, 10)); function a(b, c) { const d = b + c; return d; } console.log(a(5, 10)); 意味は等価 107文字 75文字
  16. 4. JavaScriptを削る - JavaScript 特有の圧縮 → mangling function calculateSum(a, b)

    { const result = a + b; return result; } console.log(calculateSum(5, 10)); function a(b, c) { const d = b + c; return d; } console.log(a(5, 10)); 意味は等価 107文字 75文字 mangling
  17. 4. JavaScriptを削る - 意味を変えずに圧縮する - テキストとしての圧縮 (セクション2を参照) + JavaScript 特有の圧縮

    - 使わないコードをなるべく含めない - ある場合しか使わないコードは遅延ロードさせる
  18. 4. JavaScriptを削る - 意味を変えずに圧縮する - テキストとしての圧縮 (セクション2を参照) + JavaScript 特有の圧縮

    - 使わないコードをなるべく含めない - ある場合しか使わないコードは遅延ロードさせる - 軽量なライブラリを用いる - bundlephobia などでライブラリのサイズを調べられる - 各種 Bundle Analyzer で実際のサイズを調べる
  19. 4. JavaScriptを削る - 重要な概念: Tree shaking ライブラリA 関数α 関数β 関数γ

    関数δ 10KB 20KB 40KB 30KB 100KB これだけ使う 無駄じゃね??
  20. 4. JavaScriptを削る - 重要な概念: Tree shaking ライブラリA 関数α 関数β 関数γ

    関数δ 10KB 20KB 40KB 30KB 100KB これだけ使う 無駄じゃね?? Tree shaking すると...
  21. 4. JavaScriptを削る - 重要な概念: Tree shaking ライブラリA 関数β 20KB 20KB

    これだけ使う 必要な分しかない!! 関数α 関数γ 関数δ 10KB 40KB 30KB
  22. 5. キャッシュを使う revalidate する staleキャッシュが最新のモノだった場合 ETag=”ABCDE” 304 Not Modified 最新のモノと

    ETagが同じことを確認 今のキャッシュが最新で それを fresh として良いの意味
  23. 5. キャッシュを使う - レスポンス時※ の Cache-Control の主要なディレクティブ - max-age /

    s-maxage ※リクエスト時の Cache-Control もあってややこしいが、ここではレスポンス時のもののみを扱う
  24. 5. キャッシュを使う - レスポンス時※ の Cache-Control の主要なディレクティブ - max-age /

    s-maxage - no-cache ※リクエスト時の Cache-Control もあってややこしいが、ここではレスポンス時のもののみを扱う
  25. 5. キャッシュを使う - レスポンス時※ の Cache-Control の主要なディレクティブ - max-age /

    s-maxage - no-cache - public / private ※リクエスト時の Cache-Control もあってややこしいが、ここではレスポンス時のもののみを扱う
  26. 5. キャッシュを使う - レスポンス時※ の Cache-Control の主要なディレクティブ - max-age /

    s-maxage - no-cache - public / private - no-store ※リクエスト時の Cache-Control もあってややこしいが、ここではレスポンス時のもののみを扱う
  27. 5. キャッシュを使う - レスポンス時※ の Cache-Control の主要なディレクティブ - max-age /

    s-maxage - no-cache - public / private - no-store - must-revalidate / stale-while-revalidate / stale-if-error ※リクエスト時の Cache-Control もあってややこしいが、ここではレスポンス時のもののみを扱う
  28. 5. キャッシュを使う - レスポンス時※ の Cache-Control の主要なディレクティブ - max-age /

    s-maxage - no-cache - public / private - no-store - must-revalidate / stale-while-revalidate / stale-if-error - 全部話します!! ※リクエスト時の Cache-Control もあってややこしいが、ここではレスポンス時のもののみを扱う
  29. 5. キャッシュを使う - 有効期限 (fresh である期間) を指定するディレクティブ → max-age /

    s-maxage (共有キャッシュ用) Cache-Control: max-age=86400, s-maxage=86400 有効期限を86400秒 (=1日) に指定する Cache-Control ヘッダ
  30. 5. キャッシュを使う - max-age = 0 にすると最初から stale になる -

    no-cache は max-age=0 と基本同じ Cache-Control: no-cache _ キャッシュに明示的な revalidate が必要であることを示す Cache-Control ヘッダ
  31. 5. キャッシュを使う - キャッシュを共有してよいかを指定するディレクティブ - public (共有OK) / private (共有NG)

    Cache-Control: private リソースが共有部分でキャッシュできないことを示す Cache-Control ヘッダ (ユーザー固有のページ /me など)
  32. 5. キャッシュを使う - キャッシュの revalidate 戦略を指定するディレクティブ - must-revalidate - stale-while-revalidate

    - stale-if-error - etc… Cache-Control: max-age=604800, stale-while-revalidate=86400 1週間キャッシュでき、その後1日はレスポンスをキャッシュに使ってよいことを示す Cache-Control ヘッダ
  33. 5. キャッシュを使う - キャッシュの revalidate 戦略を指定するディレクティブ - must-revalidate - stale-while-revalidate

    - stale-if-error - etc… Cache-Control: max-age=604800, stale-while-revalidate=86400 1週間キャッシュでき、その後1日はレスポンスをキャッシュに使ってよいことを示す Cache-Control ヘッダ 意味わかるよね?
  34. 5. キャッシュを使う オススメの Cache-Control 戦略 - 静的リソースは public, max-age=31536000 (1年)

    とか - ただしバージョンごとにファイル名などで区別がつかないとアプデの度に壊れる - ファイル名を固定したい場合は max-age=0, stale-while-revalidate=86400 など - 動的リソース (APIリクエストとか) は private, max-age=0 - 余裕があれば ETag を用意できると良いかも - 共有されてよいものなら public でも良い
  35. 5. キャッシュを使う ー おまけ - ここでは触れなかったキャッシュ (広い意味で) - JavaScript によるインメモリキャッシュ

    - ServiceWorker によるキャッシュ - PWA による事前ダウンロード - CDN でのキャッシュ (Webフロントなのかは不明) - リバースプロキシでのキャッシュ (もうWebサーバーかもしれない) - キャッシュは非常に奥深い (そして難しい)
  36. まとめ - セクション1 画像は WEBP かつ適切なサイズで配信する - セクション2 テキストは minify

    + brotli 圧縮する - セクション3 ウェブセーフフォントを使う - セクション4 現代的ビルドツールと軽いライブラリを使う - セクション5 Cache-Control を上手く使う