ゆめさくエンジニア研究所
技術解説2026.10.107分で読めます

部品はそのまま、書き出し方だけ変えたら PSI 50点台が 90点台になった

ゆめさく

広告の LP とスクールの HP を、React の部品は1つも書き直さずに Astro の書き出しへ移しました。速くなった理由より大事だったのは、1行で戻せる形にしたことです。

目次5項目
  1. 料金ページの LCP が 10秒を超えていた
  2. 遅いのはサーバーではなかった
  3. 部品は触らず、書き出し方だけ変える
  4. 1行で戻せるから、1ページずつ切り替えられた
  5. 壊していないことは、目ではなく機械で確かめた

ゆめさくです。このサイトを作っています。

ここ数日、広告の LP とスクールの HP、それから記事置き場の /lab を、React Router の SSR から Astro の静的な書き出しに移していました。その話をします。

料金ページの LCP が 10秒を超えていた

きっかけは PageSpeed Insights (PSI) の点数です。スマホで測ると、/lab の一覧が 55点、/women の料金ページは 41点でした。料金ページは一番大きな要素が出るまで (LCP) 10.8秒かかっていました。

広告から来た人は、待ってくれません。10秒白い画面を見せたら、ほとんど帰ります。

同じ広告の LP を、移す前と後でスマホ相当の条件で開いて並べると、こうなります。前の版は 3秒まで真っ白で、後の版は 1秒の時点でボタンまで出ています。

同じ LP を移す前と後で開いたときの画面を、時刻ごとに並べたもの
同じ LP を移す前と後で開いたときの画面を、時刻ごとに並べたもの

遅いのはサーバーではなかった

最初はサーバーを疑いました。でも測ると、HTML はキャッシュから 0.05秒ほどで届いていました。今日本番で測り直しても 0.05〜0.08秒です。

遅いのはブラウザの側でした。Google Fonts の和文の CSS が大きく、読み終わるまで文字が描かれない。そのうえ広告の LP は、ほぼ読むだけのページなのに、JS を 40本近く読んでいました。アプリ全体の React を丸ごと動かしていたからです。

ここで原因がサーバーだったら、キャッシュや問い合わせを直すほうが先でした。測ってから手を決めたので、作り替えに踏み切れました。

部品は触らず、書き出し方だけ変える

LP のデザインと文は、何度も直してきた資産です。書き直せば、どこかが必ずずれます。

なので部品は1つも書き直していません。Astro は React の部品を読み込んで、ビルドのときに HTML にしてくれます。ブラウザには React を送りません。数字のカウントアップや動画の遅延読み込みのような動く所は、部品に目印を付けて、素の JS で動かしました。

TypeScript

// 目印の付いた要素だけを、素の JS で動かす
document
  .querySelectorAll<HTMLElement>("[data-countup]")
  .forEach((el) => startCountUp(el));

和文フォントは、最初の画面に出る字だけを切り出して自前で持つ形にしました。最初は「、」や「。」が漏れて、その字だけ端末の字で出ていました。いまは1字でも漏れたらテストが落ちます。

数字にするとこうです。

移す前と後の Lighthouse の点、LCP、JS の本数、転送量の比較
移す前と後の Lighthouse の点、LCP、JS の本数、転送量の比較

この手の点は測るたびにぶれます。同じページで 91点の次に 70点が出たこともあるので、3回測って真ん中を見ています。

1行で戻せるから、1ページずつ切り替えられた

全部を一度には移せません。ログイン中の画面や管理者の編集、フォームの送信は、今のアプリでしか動かないからです。

そこで、書き出した HTML を返す小さな Worker を前に置きました。書き出していない URL、ログイン中の人、編集モード、POST は、Cloudflare の service binding で今のアプリへそのまま渡します。

前に置いた Worker が、書き出した HTML を返すか今のアプリへ渡すかを決める図
前に置いた Worker が、書き出した HTML を返すか今のアプリへ渡すかを決める図

返すページは LIVE という一覧で決めています。

TypeScript

// LIVE に入れたページだけ、書き出した HTML を返す
if (!LIVE.has(url.pathname)) return env.APP.fetch(request);
return env.ASSETS.fetch(request);

これがこの仕事のミソでした。1ページ足して、本番で確かめて、次を足す。おかしければ一覧から外して配備し直すだけで、今のアプリのページに戻ります。今のアプリ側のページは消していません。

戻しが1行だと、切り替えの判断が軽くなります。結局、広告の LP、HP、/lab、用語集まで、数日で順に切り替えられました。

壊していないことは、目ではなく機械で確かめた

LP で一番怖いのは、見た目より計測が壊れることです。広告のピクセルや LINE ボタンの記録が1つ欠けても、画面を見ただけでは分かりません。

そこで、本番の今のページと書き出した HTML を同じ広告のパラメータで開いて、文字の差、ボタンの位置、ページの高さ、送られる計測のリクエストを比べるスクリプトを書きました。移した7ページすべてで文字の差は 0行、計測のリクエストも同じでした。わざと計測の欄を壊して「差あり」と出ることも見ています。

それでも事故はありました。画像の住所を書き換える処理が単純な文字列の置換で、絶対の URL の途中まで書き換えてしまい、ロゴの住所が /women/women/ という存在しない場所になっていました。正規表現を直すだけでなく、書き出した HTML にその形が残ったらビルドを止める検査を足しました。直した場所ではなく、壊れた結果のほうを見張っておけば、次に誰がどこを触っても止まります。

もう1つ、配備直後の確かめがたまに落ちました。1秒以内だと Cloudflare の各所に新しい版が行き渡る前で、前の版が返ってくるのです。いまは 3秒おきに見て、60秒まで待つようにしています。

予約の暦のように中で動くものが多いページは、まだページごと React を動かしていて、軽くなりきっていません。動く所だけを切り出すのが次の仕事です。

相談

個別カウンセリング

受講を決める前に、オンラインで60分、人に相談できます。無料で、希望した人だけが予約する形です。

たとえば次のような相談です。

  • 自分の生活 (仕事・家庭) で学習時間を確保できるか
  • どの職種を目指すのがよさそうか
  • 他社と迷っている

状況を聞いたうえで、ほかのスクールのほうが合うと思えば、そう伝えます。

無料体験授業と個別カウンセリングの中身と申し込み方を読む

ゆめさくエンジニア研究所は、プログラミングスクール「ゆめさくエンジニア」の運営側が書いているメディアです。

X で共有する

次の記事

次に読むなら

記事をもっと見る