Web Speed Hackathon 2025に参加してました

2025-05-12

2025年3月22日(土)~2025年3月23日(日)に開催されたWeb Speed Hackathonに参加していました。

参加してから記事を書こう書こうと思い過ごしていたら、いつの間にかイベントから二か月くらい経ちそうになっていそうです。いまさらながらではありますが、参加記事を書いていこうと思います。これは自分が悪いのですが、若干情報の鮮度が悪く、自分の記憶もあいまいとなってしまっているため曖昧な部分もありそうですが解説もしていきたいと思います。

お題

今回のテーマは、架空の動画配信サービス「AREMA」です。 レギュレーションを守った上で、AREMA のパフォーマンスを改善してください。

どこかで見たことのある動画配信サービスの非常に重たいWEBアプリケーションを改善していきます。

Arema

実際に行った内容をコードを踏まえながら書いていますが、実際の時系列とは違っています。ご了承を。

webpack-bundle-analyzerとか設定とか

とりあえず手始めにバンドルサイズの確認や設定を変更していきます。webpack.config.mjsに以下のコードを追加してバンドルサイズを見てみます。

workspaces/client/webpack.config.mjs

import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

const config = {
  // ...
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
    new webpack.EnvironmentPlugin({ API_BASE_URL: '/api', NODE_ENV: 'production' }),
    new BundleAnalyzerPlugin({ analyzerMode: 'static', openAnalyzer: true }),
  ],
}

https://github.com/jdkfx/web-speed-hackathon-2025/commit/17d0aa572ab582e1bdde8304b7768445c411f7e7 https://github.com/jdkfx/web-speed-hackathon-2025/commit/3601a4eb1247c0fa9aaafec38378f17422f3d03c

bundle-analyzer

バンドルサイズの確認をしてみます。ffmpegiconifyが結構気になりますね。

画像関連のパフォーマンス

とりあえず定石となりつつある画像の最適化をやります。画像はWEBページの中でもサイズが大きくなりがちで、最適化することで読み込み速度の向上などに効果があります。

まずは画像タグにloading='lazy'を追加してみます。

workspaces/client/src/features/layout/components/Layout.tsx

<Link className="block flex w-[188px] items-center justify-center px-[8px]" to="/">
  <img alt="AREMA" className="object-contain" height={36} loading='lazy' src="/public/arema.svg" width={98} />
</Link>

https://github.com/jdkfx/web-speed-hackathon-2025/commit/f813bb4f0620212c02c4d9543e227e34980a0575

こうすることで画像の遅延読み込みが有効になり、画面外の画像表示を後回しにして最初に表示される範囲のレンダリングが速くなります。

自作ツールやネット上で圧縮できるツールを用いて画像サイズを小さくしました。後々考えればavif形式にすればもっと画像サイズを小さくできたなと思いましたし、jpeg形式の画像を別の形式に変換出来たらよかったなと思っています。

aspect-ratioを追加しました。画像の読み込み前に高さなどが確保されるため、画像が後から表示されてもレイアウトのズレなどを回避することができます。

import { ReactNode } from 'react';

interface Props {
  children: ReactNode;
  ratioHeight: number;
  ratioWidth: number;
}

export const AspectRatio = ({ children, ratioHeight, ratioWidth }: Props) => {
  return (
    <div className="relative w-full" style={{ aspectRatio: `${ratioWidth} / ${ratioHeight}` }}>
      {children}
    </div>
  );
};

https://github.com/jdkfx/web-speed-hackathon-2025/commit/f50168507e431fa5377f219848eaedc8e32348d7

キャッシュ

キャッシュの設定がno-storeになっていたため、設定を変更しました。

async function main() {
  await initializeDatabase();

  const app = fastify();

  app.addHook('onSend', async (_req, reply) => {
    reply.header('cache-control', 'public, max-age=31536000');
  });
  
  // ...
}

https://github.com/jdkfx/web-speed-hackathon-2025/commit/083c3bbfe4ccaf6c0c15fc190037855c6bee34ab

iconifyをCDN読み込みに

webpack-bundle-analyzerで確認したiconifyをCDNで読み込むようにしてあげました。これで少しはバンドルサイズが小さくなると思います。

presetIcons({
  collections: {
    bi: () => fetch('https://cdn.jsdelivr.net/npm/@iconify-json/bi/icons.json')
      .then(response => response.json())
      .then(data => data as IconifyJSON),
    'fa-regular': () => fetch('https://cdn.jsdelivr.net/npm/@iconify-json/fa-regular/icons.json')
      .then(response => response.json())
      .then(data => data as IconifyJSON),
    'fa-solid': () => fetch('https://cdn.jsdelivr.net/npm/@iconify-json/fa-solid/icons.json')
      .then(response => response.json())
      .then(data => data as IconifyJSON),
    'line-md': () => fetch('https://cdn.jsdelivr.net/npm/@iconify-json/line-md/icons.json')
      .then(response => response.json())
      .then(data => data as IconifyJSON),
    'material-symbols': () => fetch('https://cdn.jsdelivr.net/npm/@iconify-json/material-symbols/icons.json')
        .then(response => response.json())
        .then(data => data as IconifyJSON),
  },
}),

https://github.com/jdkfx/web-speed-hackathon-2025/commit/27de3331d871d8052b209861f14e4128771ae34a

p-min-delayの削除

Promiseの完了を最低1000ms遅らせるように実装されていたのでp-min-delayを削除してPromiseの完了を遅らせないようにします。

{
  index: true,
  async lazy() {
    const { HomePage, prefetch } = await import('@wsh-2025/client/src/pages/home/components/HomePage'); // p-min-delayを取り外す
    return {
      Component: HomePage,
      async loader() {
        return await prefetch(store);
      },
    };
  },
},

https://github.com/jdkfx/web-speed-hackathon-2025/commit/f01bd5a9382e359844040454e86529930ac6f194

結果

最終的なスコアと順位は以下のような結果になりました。レギュレーションチェックなどは行われていないため、実際はレギュレーションで落ちている可能性もありますが、この人数の中でこの順位まで登れたのはよかったです。

スコア

結果

反省

一応これ以外にもいろいろとやってみたことはありますし、やったけど効果がなかったのではないかなと思うものもあります。 やってみたかったこととしては以下のようなものがあったので、今後時間を見つけて挑戦してみようと考えています。

  • jpeg形式の画像をwebp形式の画像に変換
    • 変換後になぜか読み込みされない問題が起きてしまったためRevertした
  • ffmpegで生成するサムネイル画像をwebp形式の画像として生成したかった
    • webp形式の画像として生成できるように挑戦してみましたが、うまくいかず。
    • 動画を読み込んで1秒ごとにサムネイル画像を生成するような処理を行っているため、これを事前に行うようにしてあげればよかったという話でした。
  • データ量の多いライブラリの整理
    • react-dom/reactをpreactに
    • react-routerをwouterに

DevToolsのlighthouseの結果をしっかりと読み込んで問題となっている個所を特定できたらよかったかなというのも今回感じた反省点ではあります。 また次回開催された際には、より高得点をとれるように精進していきます。

Article written by.
Written by
Haruki Tazoe
November 17, 1999

Copyright © 2025 - All right reserved