Web Speed Hackathon 2025に参加してました
2025-05-12
2025年3月22日(土)~2025年3月23日(日)に開催されたWeb Speed Hackathonに参加していました。
参加してから記事を書こう書こうと思い過ごしていたら、いつの間にかイベントから二か月くらい経ちそうになっていそうです。いまさらながらではありますが、参加記事を書いていこうと思います。これは自分が悪いのですが、若干情報の鮮度が悪く、自分の記憶もあいまいとなってしまっているため曖昧な部分もありそうですが解説もしていきたいと思います。
お題
今回のテーマは、架空の動画配信サービス「AREMA」です。 レギュレーションを守った上で、AREMA のパフォーマンスを改善してください。
どこかで見たことのある動画配信サービスの非常に重たいWEBアプリケーションを改善していきます。
実際に行った内容をコードを踏まえながら書いていますが、実際の時系列とは違っています。ご了承を。
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
バンドルサイズの確認をしてみます。ffmpeg
とiconify
が結構気になりますね。
画像関連のパフォーマンス
とりあえず定石となりつつある画像の最適化をやります。画像は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形式の画像を別の形式に変換出来たらよかったなと思っています。
- svg形式からwebp形式に変換
- jpeg形式の画像を圧縮
- base64エンコードで生成されたpng形式の画像をwebp形式に変換
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の結果をしっかりと読み込んで問題となっている個所を特定できたらよかったかなというのも今回感じた反省点ではあります。 また次回開催された際には、より高得点をとれるように精進していきます。
