Web Speed Hackathon2021 に参加しました

Cyber Agentさんが主催されたWeb Speed Hackathon2021 に参加したので、やったことを殴り書きでまとめてみようと思います。

参加方法

Cyber Agentのサイトから参加登録をします。特に面接などはありませんでした。

ハッカソン当日

お題は「 めちゃくちゃ遅いSNSを早くする」でした。

どれくらい遅いかというと、lighthouseのスコアが0になるくらいには遅かったです(スコアを0にするのも大変だったそうです)。

二日間を通してlighthouseのperformanceをどれだけ高められるかという勝負でした。

それでは自分がやったことをまとめていきます。

1日目
  • mode: developmentmode: productionに変更。production モードにするだけでWebpackが諸々をある程度は最適化してくれます。
  • Webpackのsourcemapfalse もしくは process.env.NODE_ENV === 'production' ? false : trueのようにし、productionモードではJSのソースマップが展開されないように設定した
  • JSとCSSを最適化 する
  • tailwindcssのpurgeと言うプロパティを設定する。これを使うと tailwindcssで使われていないクラスを削除してくれます。tailswindcss自体触ったことなかったのですが、yarn build した時にターミナルに警告文が出ていたので気がつきました。以下のように
module.exports = {
  darkMode: false,
  purge: {
  enabled: true,
    content: [
      './src/**/*.jsx',
    ]
  }
};
  • 重たいmomentを軽量のdayjsに置き換える。
  • 重たいlodashlodash-esに置き換える。

1日目は大体これくらいで終わりました。この時点でスコアが確か60後半とかまで出ていた気がします。(あれ、思ったよりも低いし僕の最終スコアって確か51とかだった気が…)

2日目

2日目は主に画像や音声、gifの最適化に費やしました。

module.exports = {
    plugins: [
        new CopyWebpackPlugin([{
            from: "./public/images/*.png",  // 変更元のディレクトリの場所と拡張子を指定
            to: "./public/images/[name].webp"  //  webp化した画像を吐き出すディレクトリの場所を指定
        }]),
        new ImageminWebpackPlugin({
            plugins: [
                ImageminWebP({  //  quelity を設定。激しい画質落ちがなかったので1にした
                    quality: 1
                })
            ]
        })
    ]
};
  • <img>タグに<img loading="lazy">にして遅延読み込みを行う(ブラウザがChromeなので作用します)。
  • 音声に関してはGUI上で最適化してくれるサイトがあったのでこれで済ませました。ファイルサイズは13MBくらいでした(重い…)
  • gifをwebmに変換まではしたのですが、なんかうまくいかなかったので、仕方なくgifを最適化するGUIツールを使いました。ファイルサイズは100MBでした。(重すぎるだろ…) ちなみに、競技終了後になってから.gif拡張子を正しく.webmに変えられていなかった, <video>タグへ変更していなかったと気がつきました。悔しい。

  • JavaSctiptで並列処理していたところを並行化しました。

async function insertSeeds() {
  await ProfileImage.bulkCreate(profileImages, { logging: false }),
  await Image.bulkCreate(images, { logging: false }),
  await Movie.bulkCreate(movies, { logging: false }),
  await Sound.bulkCreate(sounds, { logging: false }),
  await User.bulkCreate(users, { logging: false }),
  await Post.bulkCreate(posts, { logging: false }),
  await PostsImagesRelation.bulkCreate(postsImagesRelation, { logging: false }),
  await Comment.bulkCreate(comments, { logging: false })
  ])
}

これをこう↓

async function insertSeeds() {
  await Promise.all([
    ProfileImage.bulkCreate(profileImages, { logging: false }),
    Image.bulkCreate(images, { logging: false }),
    Movie.bulkCreate(movies, { logging: false }),
    Sound.bulkCreate(sounds, { logging: false }),
    User.bulkCreate(users, { logging: false }),
    Post.bulkCreate(posts, { logging: false }),
    PostsImagesRelation.bulkCreate(postsImagesRelation, { logging: false }),
    Comment.bulkCreate(comments, { logging: false })
  ])
}
  • <scirpt defer>をつけることでJSを並行で取得しています。詳しいHTML仕様書はこちら

こんなことをしたあたりで時間切れになりました。

結果発表

気になる最終スコアですが、なんと…………………………

気になる結果は…

ど゛う゛し゛て゛な゛ん゛だ゛よ゛お゛お゛ぉ゛お゛!゛!゛!゛ん゛あ゛あ゛あ゛あ゛あ゛ぁ゛ぁ゛あ゛あ゛!゛!゛!゛!゛

なんか1日目よりも下がってる…ちゃんと画像系の最適化もしたはず何になぜ…

これも終わってから気づいたのですが、不要になったライブラリを削除していないと気がつきました。 しかも今回僕は色々なライブラリを試しまくっていました。不要になったライブラリを削除したところスコアが上がりました。悔しい…

競技終了後の解説で山ほど改善できた点があったことを知り、ずっと「すげぇ…」と言っていました。 ちなみに1位の人は600天超えでした。コードを拝見したのですが、擬似サーバーサイドレンダリング?などをしているようで「すげぇ…」となりました。

まとめ

初めてのパフォーマンスチューニングでしたが、めちゃくちゃ楽しかったです。 もっとパフォチュを知りたいと思ったし、ブラウザについての興味も湧いてきました。とりあえず『超速本』を読んで勉強します。 最後に Cyber Agentさん、今回はこのような素晴らしいハッカソンを開催していただきありがとうございました!