Reactナンモワカランけど Web Speed Hackathon 2023 参加したらとても楽しかった
Web Speed Hackathon 2023に参加しました。楽しかった。
コンテストの詳細は省きますが、やたら動作の重たいフロントエンドアプリをめっちゃ軽くしようぜ!というものです。
自分はいわゆる中堅SIerの中堅社員。現場の開発作業を担当しない立場になって久しい。Vue.jsは昔案件で扱った経験があり、基礎知識はある程度。でも、ReactもTypeScriptもナンモワカランへなちょこです。
詳細な解法解説は天才にお任せするとして、へなちょこな自分は、コンテスト初心者向けに、どんな準備をすればよいのかということと、当日の心構えを記す。
目次
- 結果
- 準備したこと
- 当日の反省と心構え
- おまけの記録
結果
スコア計測者73名中、24位でした。
画像では14位。自分より上位の学生さん10名が順位決定のためにレギュレーションチェック入り、違反によりランキングから除外されているのもカウントしての順位です。自分もチェックされてたらアウトなとこあったかもしれない。
上位は400点とか取ってました。学生さんでも300点オーバーとか、化け物かと思いましたね。(褒め言葉)
初回計測スコアと最終スコアはこんな感じ
スコアの変遷
準備したこと
開催5日前に知りました。(AHC018がおわった直後…)
部署で呼びかけたら、UIEngineerスペシャリストの肩書を持つKさんが参加表明をしてくれたので、会社のSlackにチャンネルを作って準備スタート。休み時間や業務時間外に、各々準備をすすめました。
自分は主に過去問の解説記事をあさりつつ、すぐできそうなのを実際に試したり。「推測するな、計測せよ」のお言葉に従って、開発者ツールのパフォーマンスタブを眺めていました。
解説記事漁りは本当に為になるので、コンテスト初心者は必ず読んだ方がいい。 これでコンテストで何をすればいいか、だいたいイメージできた気がする。
あとは、参加の目標の一つに参加記事を書くことを挙げていたので、Kさんを真似してZenn CLIを準備。準備した割に、当日記録しながら進める余裕が全くなかったけれど。
当日の反省と心構え
いざ、リポジトリ公開。
…あの、webpack.config.jsがありません。そうですか、Viteなんですね。バックエンドはGraphQLですか。どちらも名前は聞いたことあったけど、実物は初めまして。ふーん、こんな風に実装するのね。
nvmでnodeを切り替え忘れていたり、ts-nodeが入ってなくてインストールしたり。初回ビルドまでに時間が結構かかってしまい、初回計測はリポジトリ公開から1時間以上経過していた。
さて、ここからが戦いのはじまり。
自分の対応は定番のものにとどまってるし、詳細は他の天才にお任せすることにして、どういう感じでやってたか、あとから振り返ってこうすればよかったとかの心構えを、自分の反省も含めてコンテスト初心者向けに書いていきたいと思います。
基本のキ:推測するな、計測せよ
画面立ち上げてずっとCPUがファンファンいってたのはReactのDOMの構造が複雑なのかも?とか思いこんでずっと解決できずに時間がかなりかかってしまいました。結局その時は解決できず、別件でパフォーマンスタブを眺めているときに原因を突き止めることができましたが。思い込みで調べ始めるのは効果的ではない!
基本のホ: 落ち着いて1つ1つ対応していこう
1つのことを調べ始めると、そこから派生してあれもこれもと対応したいことが思いついて、でもスキルが追い付かなくて、あわわわってなりました。
思いついたものは忘れないようにメモしておきつつ、1つ1つ対応していくのがよさそう。
たくさん対応を入れ込んだ状態でスコア計測すると、どれが効果的だったか分からなくなります。たくさん設定を変えて、さあ動かしてみようとすると、急にアプリが動かなくなったりして、切り戻しをどこまでやればいいのか分からなくなることもありますし。
また、1つのことを調べて分からない状態が続くと、永遠に調べ続けてしまう罠も潜んでいました。時間を決めて切り上げたり、いったんリフレッシュして違う視点で考えるのも大事。ちゃんと休憩しましょう!
基本のン:スコアに大きく影響しない改善はやらない
Lighthouseの指標がそのまま得点になるので、(コンテスト中は)スコアに響かない修正はやらない方がよいです。
Lighthouseの計測結果には、スコアに大きく影響する対応と、影響しないけどこうした方がいいっていう対応の両方が表示されていて、後者の方で「これならすぐ対応できそう」みたいなのが目についちゃうと、対応したくなってきちゃいます。が、はっきりいってスコアにほぼ影響しない対応に時間をかけるのはもったいないです。
また、運営による計測結果のケースごとの得点もしっかり見た方がよさそう。ある程度得点が高いケースをもっと高く上げるよりも、得点がかなり低いケースをそれなりの点に持っていく方が多分簡単です。上位の方のスコアを見ると、後半のケースは計測できていなくて、前半だけでスコアを稼いでいます。伸びしろがあるケースに関するコンポーネントや処理を見て、ピンポイントでスコアを伸ばした方がいいのかもと思いました。(正しいかはわからない)
シークレットタブ&低速ネットワークで動かそう
Lighthouseはシークレットタブで計測したほうがよいです。なぜならChromeの拡張の影響を受けないから。普通のタブで計測すると、拡張機能のJSファイルなども一緒に読み込んでしまうので正確な計測ができません。
また、自宅Wi-Fiだと通信負荷が分かりにくいので、開発者ツールのスロットリングで高速3Gを選択して、疑似的に通信状況が悪い状態で計測すべし。
数MBでもかなり重いな・・・って実感できて、手を入れるべきところが明らかになります。
分からないことはChatGPTも活用しちゃう
「GraphQLでレスポンスを圧縮したんだけどどうしたらいい?Koaを使ってるんだけど」
「このソースコードにパフォーマンス悪化しうる箇所はない?」
みたいにふわっとした質問でもそれっぽい回答を返してくれました。
何を調べればいいのかわからないときにキーワードが出てくるのはとてもありがたかった。
そのあとちゃんとドキュメントとか確認した方がよいです。
また、コードをそのままコピペして、これを非同期で処理したいとか聞くとコードで返してくれるのも、ES20xxすらおぼつかない自分にはとてもよかった。業務だったらコードをそのままコピペしたら機密上イカンので、コンテストならではの便利な使い方かもしれない。ヘンテコな実装も返してくるので、ある程度理解してないと余計混乱するかもしれない。
計測結果のエビデンスは取っておきたい
自分のブラウザで計測したLighthouseのデータはJsonで保存できます。
今回知らない技術要素だらけで余裕がなく、対応ごとの改善スコアをまったく記録できなかったのは反省点。
オフィシャルな計測は割と大き目な幅で上下するので、同じビルドイメージで計測しても、下がってしまうときがありました。下がるとテンションダウンしてしまいますが、手元の計測で上がっていれば自信にもなるので、ちゃんと記録しておきたかった。
おわりに
2日間思ったよりのめりこんでしまって大変だったけど、ぎゅうぎゅう新しい知識をインプットできてとても濃密な2日間でした。とにかく楽しかった。
今これを書きながら実装を見直していると、あれはこうすればよかったんじゃない?とか、いろいろ思いついてきますね。それもすべてメモを取りつつ、延長戦用のリーダーボードが公開されたら再挑戦したいと思っています。
部署でもフロントエンドのWeb開発案件が多いので、延長戦や他の過去問をみんなでわいわい解きたいね、とKさんと話したりしました。これはちゃんと準備していこう。
運営の方には感謝です。準備大変だったでしょう。でもサイバーエージェントさんの技術力がとてもすごいのがよくわかりました。またやってくださいね。
以下、メモ代わりに対応詳細を記録
準備したこと詳細
- 開発環境の準備
- Windows上でやるか、WSLのUbuntu使うか迷った。最終的にPC負荷を考えてWindows上でやることにした。
- webpack-bundle-analyzerを使ってみる
- 開発者ツールのパフォーマンスタブを確認
- webp変換ライブラリ作成
- フォント関連ツールインストール
- WOFFコンバータとサブセットフォントメーカー
- 過去問の解説記事をあさる これ超大事
- 2022年過去問を触る
- 昨年末の社内AdventCalendar企画で中途半端に挑戦していたものの続きを少々。FCPが初めて緑色になってにっこり。
- lodashやめる
- moment.jsやめる
- jsをchunkする
- 昨年末の社内AdventCalendar企画で中途半端に挑戦していたものの続きを少々。FCPが初めて緑色になってにっこり。
- 今回アナウンスされた環境の準備
- Node v18 をnvmでインストール
- pnpm はて・・・何者ぞ?過去問でyarn使い始めたばかりだが?
- fly.io
- 事前にCyberTicketのデプロイを試し、デプロイの遅さを実感。ローカルビルドの方が速かった。
- Zenn CLI準備
当日やったこと詳細
-
svg圧縮
- とにかく巨大だったlogo.svgを適当なWebサイトでサイズ削減
-
jpgをwebp変換
- 画像そのものはすぐに変換できたけど、アプリ側はjpgを読んじゃう。いろいろやってみて、ここでかなり時間を食ってしまった。
※databaseの中にデータがあるみたい。dataをinitializeしよう→ん?なんか失敗する→いろいろ直してwebpは出るようになったけど、他の画面の挙動がおかしい→dataをなんとかするのは諦めて切り戻し→アプリ側の実装をいじって拡張子jpgをwebpに変換して表示する小手先の対応でごまかす
今考えれば、updateしちゃえばよかった→やってみる
- 画像そのものはすぐに変換できたけど、アプリ側はjpgを読んじゃう。いろいろやってみて、ここでかなり時間を食ってしまった。
-
mp4圧縮
- mp4はこれもどっかの適当なWebサイトで質が落ちすぎない程度に圧縮してサイズ削減
- サムネも作ってクリックされたら動画を流す方がFCPがよかったと思うが面倒だった
-
bundle analyzerいれる
- rollup-plugin-visualizerでhtml出力
-
bundleサイズ減らし
- lodashやめる
- date-time-format-timezone消す
- core-jsらへんを消す。最新版のChromeに対応すればよいので。
- vite-plugin-compressionで圧縮
- rollup-plugin-terserで圧縮
-
Viteオプション見直し
- 無駄な設定を削除(ドキュメントよく読む)
-
バックエンド、Koa-compressでgzip圧縮
-
br: false
のオプションをつけないとサーバすぐ落ちちゃうので注意
-
-
Cache-control: public, max-age=適当に大きい数字
- ブラウザキャッシュは効くようになったけど、計測のLighthouseから画像リクエストが都度来てた気がする?計測のタスクが変わったらキャッシュクリアされるのかな?
-
郵便番号検索は外部API利用
- zipcodeが重たかったので、こちらのAPIに置き換えた。データ構造同じですぐできた。
- https://zipaddress.net/
-
CanvasKit.wasmを使わないようにする
- wasmだけで6MBくらいあったので、何とか使わないようにしたかった。
- canvasに置き換えたけど、imageでよかったみたい
-
CPU圧迫のTimer処理削除
- パフォーマンスタブでCPU上がりまくってる原因の箇所を突き止めTimer処理を根絶
- それまでずっとCPUファンがファンファンいっててかなりうるさかった
-
fly.ioのアプリをスケールアップ
- 256MBから512MBに。社会人なのでお金の力で解決。($0.02)
-
GraphQLでレスポンスめちゃ遅いの改善したかった
- N+1問題。分解しようとしたけどもう全然わからなくて無理だった。延長戦でやる。
ここに書いてあるのは結果だけであって、これにたどり着くまでにもっといろいろ試行錯誤していてムダが多かったなあ。運営の解説が公開されてから、じっくり延長戦取り組みたい!
Discussion