Viteはとても便利です。正直なところ、今でもそう思っています。
開発サーバーは驚くほど速く、HMR(Hot Module Replacement)は即座に反応し、設定は極めてシンプル、そしてReactがそのまま動く。Vite + React + TypeScriptで個人サイトを構築したときは、それが「正解」だと確信していました。モダンで、使い慣れていて、効率的。変える理由なんてどこにもありませんでした。
しかし、ある日、何とも言えない違和感を覚え始めたのです。
自分が「存在しない」ことに気づいた瞬間
ある時、自分が書いた記事の内容——SwiftUIのマルチプラットフォーム・アーキテクチャやReactのレンダリング戦略、アクセシビリティのパターンなど——について、AIアシスタントがどのように回答するかをテストしていました。
ところがAIは、私のコンテンツが存在することすら知らないか、あるいは私が書いた内容とは全く関係のない一般的な回答を返すだけでした。
そこで、サイト(のURL)を渡し、直接こう尋ねてみました。
「ここには何が書かれていますか?」
有益な結果は得られませんでした。
私は、クローラーと同じ方法で自分のサイトにアクセスしてみました。つまり、JavaScriptを実行しない純粋なHTTPリクエストです。返ってきたのは、実質的にこれだけでした。
<div id="root"></div>
それだけです。ただの空のシェル。私が書いてきたすべての記事、すべての考え、経験から得た知見のすべてが、実際のブラウザでしか実行できないJavaScriptパッケージの中に閉じ込められていたのです。
検索クローラーにとって、私は存在していませんでした。私のサイトを読もうとするAIにとっても、同様です。私は事実上、ウェブから姿を消していたのです。
なぜこれがSEO以上に重要な問題なのか
これは単なるSEO(検索エンジン最適化)の問題ではありません。SEOであれば、何らかの対策は可能でした。Googleも最終的にはJavaScriptをレンダリングすることを学んだからです。
しかし、今起きている変化はそれとは異なります。人々は検索エンジンを使う代わりに、ますますAIアシスタントに直接問いかけるようになっています。彼らは質問し、AIが要約された回答を提示します。AIがユーザーの代わりにウェブ上のコンテンツを読み解くのです。
もしAIがあなたのコンテンツを読めなければ、単に検索順位が下がるだけではありません。そもそも「対話」の場にすら存在できなくなるのです。
この格差は今後さらに広がっていくでしょう。誰かがAIにツールを推薦してもらったり、概念の理解を深めたり、問題を解決しようとしたとき、あなたのコンテンツがJavaScriptの壁の向こう側に隠れていれば、その回答の中にあなたの存在はゼロになります。順位も、露出も、何もありません。
自分の考えを共有し、存在感を確立しようとする開発者にとって、これは無視できない問題でした。
なぜVite + ReactのSPAでは解決できないのか
問題はアーキテクチャの根幹にあります。シングルページアプリケーション(SPA)の仕組みは以下の通りです。
- ブラウザがページをリクエストする。
- サーバーが空のHTMLシェルと巨大なJSパッケージを返す。
- JavaScriptが実行され、ようやく実際の内容がレンダリングされる。
JavaScriptを実行しないAIクローラーやHTTPクライアントは、2番目のステップで止まってしまいます。彼らが手にするのは「外側」だけで、中身が表示されることはありません。
プリレンダリング(Prerendering)を使ってパッチを当てることもできますし、私も試しました。シンプルなケースでは有効ですが、複雑な状態管理、動的な国際化(i18n)、Context Provider、クライアントサイド・ルーティングなどが絡んでくると、プリレンダリングは終わりのない戦いになります。ルートの欠落、ハイドレーション(Hydration)の不一致、トレイリングスラッシュ(末尾の /)の挙動によって言語ディレクトリ全体が消えてしまうといった現象に悩まされました。
実際、/zh/ が白い画面(空のHTML)を返す理由を突き止めるために数十分を費やしたこともあります。dist/zh/index.html は存在し、内容は正しかったにもかかわらずです。原因はCloudflare Pagesがトレイリングスラッシュを削除し、ルートディレクトリの空のシェルにフォールバックしていたことでした。これこそ、クライアントサイド・レンダリングが生み出す「不可視の故障」の一例です。
Astroへの切り替え
私は leonkang.com をAstroに移行しました。
理由はシンプルです。Astroはデフォルトで「本物のHTML」を生成するからです。AIであれ検索エンジンであれ、クローラーが /en/blog にアクセスすれば、そこに実際の内容が存在します。記事のタイトル、概要、タグ、日付。JavaScriptを介さずとも、すべてが可読な状態でそこにあります。
今回の移行で求めたもの:
- マルチリンガル対応(
/en/,/zh/,/ja/のURLルーティング)— Astroが標準でサポートしています。 - 静的出力によるAIやクローラーへの可読性 — Astroのデフォルトの挙動です。
- インタラクティブな要素にはReactを使い続ける — Astro Islandsの
client:loadディレクティブにより、Reactコンポーネントをそのまま保持できます。 - Cloudflare Pagesへのデプロイ — 公式のアダプターで設定不要です。
移行には摩擦もありました。ルーティングのパターンは変わり、思考モデルは「クライアントサイド・ナビゲーションを備えた一つのSPA」から「オプションでクライアントサイド・ハイドレーション(hydration)を備えた本物のページ群」へとシフトしました。しかし、結果はまさに求めていた通りでした。すべてのページが、実体のあるコンテンツを持つ本物のHTMLドキュメントになったのです。
今では、何かが /en/blog にアクセスすれば、7つのブログ記事とそのタイトル、概要、タグが即座に読み取れます。読みやすく、インデックス可能で、ネットワーク上に「存在」しているのです。
技術は人とコンテンツのためにある
この業界には、技術の選択をアイデンティティの一部にする傾向があります。「私はReactデベロッパーだ」「何でもViteで事足りる」「なぜ他のものが必要なのか」。
理解はできます。使い慣れていることには大きな価値がありますし、学習コストや切り替えコストも無視できません。
ですが、より誠実な見方はこうではないでしょうか。技術とは、コンテンツと人のためのインフラである。 インフラがコンテンツへの奉仕をやめたとき——つまり、自分の書いた内容が、人々が実際に使うチャネルにおいて「不可視」になってしまったとき——それはインフラを更新すべき時なのです。
サイトを立ち上げた当初は、Vite + ReactのSPAが最適解でした。しかし、自分のコンテンツをどのように届けたいかを考えたとき、今の時代には別の選択肢が適していました。
今この瞬間には、Astroが適しています。次の瞬間には、また別の何かが適しているかもしれません。それは不安定さではなく、この仕事の本質なのです。
ウェブは常に変化し、人々がコンテンツを見つける方法も変わり続けています。2年前に「十分」だった個人サイトが、今日ではむしろあなたの活動を阻害しているかもしれません。
私は、消えてしまうくらいなら、何度でもサイトを作り直す道を選びます。
このサイトはAstroで構築され、Cloudflare Pagesにデプロイされています。すべてのページは静的に生成され、英語、中国語、日本語のフルi18nに対応しています。