- Muxはmux.comとdocs.mux.comをReact Server Componentsへ移行する中で、サーバーとクライアントの実行境界を分けることがバンドルサイズとhydrationコストに直接影響することを確認した
- RSCはコンポーネントがサーバーで直接データを取得し、その結果をストリーミングできるようにするため、遅いデータ呼び出しがあっても画面の一部を先に表示できる
- 実際の移行で最大の障害になったのは、CSS-in-JSの非対応、Server ComponentsにおけるReact Contextの制約、そしてサーバー/クライアント境界を継続的に追跡しなければならない複雑さだった
- Next.js 13のapp directoryではデフォルトがServer Componentであり、ルートの
use clientを徐々に下へ移していく形で段階的導入が可能 - Suspense、
loading.js、サーバー専用ライブラリの維持、server-onlyのようなパターンは、性能上の利得が必要な箇所にだけ慎重に適用すべきであり、チームの認知コストも合わせて見積もる必要がある
MuxがRSCへ移行した範囲
- Muxはドキュメントサイトの再構築とブランド変更を進める中で、mux.comとdocs.mux.comをServer Componentsへ移行した
- React Server Componentsは実際のコードベースでも適用可能で、導入する価値があるかもしれないが、制約と複雑さも伴う
- この経験は、RSCがなぜ必要なのか、どこに適しているのか、どんな場面で難しいのか、実際のコードベースへどう段階的に取り入れられるのかを中心に整理されている
CSR、SSR/SSGの次にRSCが解決しようとする問題
- 初期のサーバーレンダリング方式は、PHPのような技術でサーバー側でデータを取得し、重いCPU処理を行ったうえで、クライアントには軽量なHTMLを渡していた
- CSR/SPAはレンダリングコードをJavaScriptとしてクライアントに送り、インタラクションを高速に処理できるようにしたが、検索エンジンがJavaScriptを実行しない場合や、サーバー側に秘密情報を保持する必要がある場合、あるいは低性能デバイスや遅い接続環境では弱点が表れる
- SSR/SSGは、Next.jsやGatsbyのようなツールを通じて、サーバーでHTMLとJavaScriptを一緒に生成してクライアントへ送る方式である
- ユーザーはHTMLをすぐに見られる
- JavaScriptが読み込まれるとサイトがインタラクティブになる
- 検索エンジンもHTMLを読める
- 従来のSSR/SSGにもコストは残っている
- ページ生成に使われたJavaScriptの大半をクライアントへ送り、クライアント側で再実行してHTMLと結合するhydrationが必要になる
- サーバーレンダリングが遅いデータベース呼び出しや大量のコード実行のせいで長引くと、ユーザーは待たされる
React Server Componentsが変えるポイント
- React Server Componentsは、クライアントではなくサーバーで実行されるReactコンポーネントである
- RSC対応フレームワークでは、コードが実行される場所を明示的に分けられる
- Server Components: サーバーでのみ実行されるべきコード
- Client Components: クライアントで実行されるべきコード
- 実行場所を分離すると、クライアントへ送るJavaScriptが減り、hydration中に実行すべき処理も減る
- Server Componentはコンポーネント内部で直接データを取得できる
- Nodeライブラリや
fetchを使える - ページレベルで
getServerSidePropsを使って一括取得し、propsとして延々と渡していくやり方を減らせる useEffectで複雑なローディング状態を管理するケースも減る
- Nodeライブラリや
- データ取得を終えたServer Componentは、その結果をクライアントへストリーミングできる
- 遅いコンポーネントを待っている間に、サイトの残りを先に表示できる
- クライアントのユーザー操作に応じてサーバーでデータを取得し、応答をストリーミングするやり方も可能だが、これは厳密にはRSCではなく、React Actionsに該当する
RSCの難しい部分
- CSS-in-JSは現在、Server Componentsでは動作しない
- MuxのRSC移行では、styled-componentsからTailwind CSSへの移行作業が最も大きな比重を占めた
- CSS-in-JSに大きく依存したコードベースであれば、別途移行作業が必要になる
- React ContextにはClient Componentsからしかアクセスできない
- Server Components間でpropsなしにデータを共有したい場合は、通常のモジュールを使うことになる可能性が高い
- Reactアプリケーションの特定のサブツリーにだけデータを制限する良いメカニズムは、Server Componentsには存在しない
- Muxのドキュメントサイトでは、Contextを使う領域はインタラクションが多く、どうせクライアントへ送る必要があったため大きな問題にはならなかった
- マーケティングサイトではテーマ共有が問題になった
- pre-footerの各コンポーネントは、緑色の背景の上に置かれていることを知っていないと、濃い緑のボーダーを使えなかった
- Contextの代わりにCSS custom propertiesを積極的に活用して回避した
- RSCは実行場所とデータ取得方法に柔軟性をもたらす一方で、そのぶん複雑さも増す
- 新しい開発者は「何がサーバーで実行され、何がクライアントで実行されるのか」を常に確認しなければならない
- PRごとに、不必要にクライアントへ送られたコードに対するフィードバックが発生する
- 開発中にサーバーログなのかブラウザログなのかを確認するため、
console.logを入れることが頻繁にあった - キャッシュも別の複雑さを加える
Next.js 13でRSCを使う基本形
- 執筆時点で本番運用可能なRSC実装は、Next.js 13のapp directoryである
- Next.js 13のapp directoryでは、デフォルトで作成するコンポーネントがServer Componentになる
- 初期状態ではページコードはクライアントへ送られない
- クライアントにはHTMLだけが渡される
- Server Componentに
asyncを付けると、コンポーネント内部でデータを取得できる - 遅いデータ取得を含むServer Componentは
React.Suspenseで包める- クライアントにはfallback UIが先に表示される
- サーバーがデータを取得してレンダリングを終えると、結果のコンポーネントがストリーミングされる
- Suspense boundaryはデータストリーミングだけでなく、ユーザー操作に応じて特定領域のhydration優先度を調整するselective hydrationにも利用できる
- クライアントで実行されるべきコードには、ファイル先頭に
"use client"を追加するonClickリスナーやuseStateのように、クライアント状態やインタラクションが必要なコンポーネントで使う"use client"が付いたコンポーネントがimportするすべてのコンポーネントもクライアントへ送られる
- RSCに対応していないライブラリはClient Componentでimportしてクライアントバンドルに含められる
- 例として
@mux/mux-player-reactをラップするClientMuxPlayerコンポーネントがある
- 例として
Server ComponentとClient Componentの使い分け
- Server Componentsは、クライアントへ送る必要のないコードに適している
- ブログ記事本文のレンダリング
- コードブロックのシンタックスハイライトのような高コスト処理
- データ取得
- Client Componentsは、ユーザー入力に反応したり、時間経過で状態が変わったりするUIに適している
useState- イベントリスナー
- クライアント側インタラクション
- アプリ全体をClient Componentsで作れば、従来のSSRフレームワークに近い挙動になる
- アプリ全体を一度にServer Componentsへ変える必要はなく、効果の大きい箇所から段階的に導入できる
実際のコードベースへ段階的に導入する3ステップ
- Muxが使ったプレイブックは3段階だった
- アプリのルートに
"use client"ディレクティブを追加する - そのディレクティブをレンダリングツリーの中で可能な限り下へ移動させる
- 性能問題が見えてきたときに高度なパターンを適用する
- アプリのルートに
- 第1段階では、Next.js 13の最上位
page.tsxに"use client"を追加して従来どおり動かす - サーバー側のデータ取得が必要なら、Client Componentの親としてServer Componentを追加する
- Server Componentがデータを取得する
- 取得したデータをClient Componentへpropsとして渡す
- 従来の
getServerSidePropsの役割を置き換えられる
- 第2段階では、
"use client"を最上位コンポーネントから子コンポーネントへ移していく- クライアントコードが不要な
<Title />ではディレクティブを外し、純粋なHTMLとして送れる - クライアントコードが必要な
<Player />ではエラーになるため"use client"を維持する
- クライアントコードが不要な
- この方法により、新しいコンポーネントや既存リファクタリング対象でServer Componentsを検討しやすくなり、バンドルサイズをある程度削減する助けになる
性能問題があるときに適用したパターン
- Muxのドキュメントサイトは大半が静的生成だが、changelog sidebarはCMSから取得している
- sidebarをSuspenseで包めば、CMS fetchが終わるまでアプリの他の部分が待つ必要はない
- Next.js 13のloading.js規約も、内部的にはSuspenseとstreamingを使っている
- 大きなライブラリをサーバー側に残したいなら、Client ComponentsとServer Componentsの配置を調整する必要がある
- 例としてシンタックスハイライトライブラリPrismをサーバー側に維持した
Client Componentの中にServer Componentを混ぜる方法
- Client Componentがimportしたコンポーネントは一緒にClient Componentになる
- Server ComponentをClient Componentの子にしたいなら、importせずに
childrenやpropsとして渡す必要がある- Server Componentはサーバーでレンダリングされる
- シリアライズされた結果がClient Componentへ渡される
- 間違ったやり方は、Client ComponentファイルでServer Componentを直接importすること
- 正しいやり方は、最も近い親のServer Componentまで戻り、そこからClient ComponentへServer Componentを子やpropとして渡すこと
1つのファイルをサーバー/クライアント半々にはできない
- 1つのファイルの半分をServer Component、半分をClient Componentにすることはできない
- Muxは機能を2つのファイルに分けるパターンをよく使っていた
CodeBlock.server.js: 大きなシンタックスハイライトライブラリをimportし、サーバーでレンダリングするCodeBlock.client.js:useStateとonClickを使い、ユーザーがコード例を切り替えられるようにする
- サーバーでレンダリングした例はpropsとしてClient Componentへ渡されるため、サーバー専用処理がクライアントバンドルへ流れ込まない
index.jsでCodeBlock.server.jsを再exportすれば、利用者はCodeBlockだけをimportすればよく、内部のサーバー/クライアント分離を意識しなくて済む
サーバーでのみ実行されることを保証する方法
- 初期には開発中、
console.logを追加してログがサーバー由来なのかブラウザ由来なのかを確認していた - サーバー専用コードがバンドルに含まれないことを保証したいなら、server-only packageをimportできる
server-onlyは、大きなライブラリや秘密鍵が誤った場所へ移動しないようにするのに有用である- Next.jsは環境変数が誤ってブラウザバンドルに含まれるのを防ぐ保護機構を提供している
- ファイル先頭の
server-onlyは保守性の面でも役立つ- 管理者はそのファイルがサーバーで実行されることをすぐに把握できる
導入判断におけるコストと利点
- React Server Componentsは無料で手に入る機能ではない
- コストにはCSS-in-JSやReact Contextの制約だけでなく、次のようなものも含まれる
- サーバーとクライアントの実行場所の理解
- hydrationの理解
- インフラコスト
- Client ComponentsとServer Componentsの混在によるコードの複雑さ
- 複雑さはバグの入り込む余地を増やし、コード保守性を下げる可能性がある
- フレームワークは複雑さを減らしてはくれるが、取り除いてはくれない
- 期待できる利点は次のとおり
- より小さいバンドルサイズ
- より高速な実行
- SEOに重要な性能改善
- 複雑でデータ量の多いサイト向けの高度なデータローディングパターン
- チームが追加の認知コストを受け入れる準備があり、性能面の利得が十分に大きいなら、RSCは適した選択肢になりうる
1件のコメント
Hacker News のコメント
サーバーサイドレンダリングでは、クライアントはすぐに表示できる HTML を受け取る。
これも実感したが、サーバーにプレーンテキストファイルを置くと、ブラウザにかなり速く届く。
.cssで終わる別のプレーンテキストファイルを置くと、ブラウザは処理方法を知っているので、ファーストビューの要素が動いて、かなり見栄えがよくなることもある。すてきな小技ではあるが、ファーストビューで読める有用なコンテンツよりは、やはり二次的なものだ。
「ハイパーテキストをもっと強力にする機能を追加しよう」が、どうして「この大きくて一貫性のない機能の山を使って、まともなアプリケーションは各自で実装してください」になったのか分からない。
RSC に入る前に、いったん立ち止まったほうがいい。
何を作ろうとしているにせよ、本物のフルスタックフレームワークや古典的な Web フレームワークなら、はるかに簡単で速く、スケーラブルに処理できる。
Rails/Django/Laravel/… に Turbolinks/Htmx/… を組み合わせてもいいし、少しだけクライアント側 JavaScript を振りかけるだけでもいい。
Elixir/Phoenix を知っているなら、複数の利点をまとめて得ることもできる。
どれだけ多くの人がツイートしていようと、RSC の方向へこれ以上進むべきではない。
業界経験が10年未満の人たちは、昔の vanilla PHP サイトで経験された基本的な問題をもう一度踏むことになるし、React コンポーネントの中にインライン SQL フックを入れている例もすでに見た。
今回はそこに偶発的複雑性まで、はるかに多く上乗せされている。
正気を保ち、実際の製品を早くリリースして、ランボルギーニ代を稼げばいい。
CORBA はローカルコンポーネントとリモートコンポーネントを混在させる方式で、成熟しており、複数の言語で動作する。
では、なぜ皆が使っていないのか? 今日の開発者の多くは聞いたことすらないだろう。
さらに別の分散コンポーネントアーキテクチャを作りたいなら、CORBA とその子孫たちがなぜ広く定着しなかったのかを学ぶべきだ。
ヒントは、隠れたコンポーネント境界が隠れた複雑性を生むということだ。
一方で、「サーバーレンダリング HTML」と「クライアントレンダリング HTML」の陣営は、どちらもうまく回っている。
Web プロジェクトごとに両方の選択肢を使えるのは、かなり幸運なことだと思う。
RSC の作業が、純粋なクライアントレンダリングアプリへの React のサポートを曇らせないことを願う。
完全なプログレッシブなリッチフロントエンドアプリがどうしても必要でない限り、ほとんどは LiveView が最小限の JavaScript で、サーバーサイドコンポーネントを通じて処理してくれる。
それだけでなく、ユーザーごとにサーバー側スレッドが付き、明示的な JavaScript ハンドラを書かなくても、ユーザーのフロントエンドへ変更を能動的にプッシュできる。
業界は10年周期の記憶喪失にかかっているように思える。
サーバーレンダリング UI から離れたのには、非常に妥当な理由があった。
もちろん検索エンジン最適化という論理は常にあるが、それが心配なら、単に伝統的なサーバーテンプレートのサイトを作ればいい。
大多数のシングルページアプリは、SSR とその複雑性をまったく必要としていない。
初期のダッシュボードインターフェースはすべて Phoenix でレンダリングし、本当に高度なクライアントインタラクションが必要なページだけ個別に React を含めていた。
最初の製品が分析ダッシュボードだったため、その状況はすぐに、ほぼダッシュボード全体へと広がり、顧客向けに公開していた API を活用して完全なシングルページアプリへ移行するのが自然だった。
当時は2016年で LiveView はなかったが、今その製品を作り直すとしても、別の判断をしたかどうかは確信がない。
ブログ記事は公開マーケティングサイトを動かすアプリケーションについてのもので、要件はかなり違うが、私たちも Elixir/Phoenix を使っていて気に入っている、という点は言っておきたかった。
自分が年を取ってきた気がする
最近のフレームワークは大きすぎて複雑すぎる
単純なWebの「Hello world」にも巨大なビルドとコンパイルのパイプラインが必要で、今ではサーバーサイドコンポーネントまで付いてくる
オーバーヘッドがどの程度なのか本当に気になる
Hello worldの例を実行するために、フロントエンドとバックエンドのフレームワークのコードを何層も通っているのか分からない
私はF5を押すだけで再ビルドされる10KBのシンプルなコンポーネントフレームワークに戻ることにする
後で生まれるかもしれない派手な機能のために、早すぎる最適化をしているようなものだ
鶏小屋を建てる前に、コアサンプルを採取して地震波モデリングをするためにエンジニアを雇うべきだと言っているのに近い
若い開発者たちが今、それに気づき始めている
私たちがSOAPやXMLのようなひどいものを捨て、より単純で使いやすい技術へ移ったように、この世代もまた複雑さは有害だということを学びつつある
もしかすると、新しい世代がまた台無しにするまでの数年だけでも、ソフトウェア開発が再び楽しくなるかもしれない
パイプラインは特定のユースケースに合わせて、必要なだけ複雑にも単純にもできる
静的ファイルだけで作ることもできるし、
esbuildコマンド1つを使う小さなMakefileでも可能で、プラグイン30個の巨大なWebpack設定にもできる作ろうとしているものの要件と複雑さに合わせて、いくらでも選べる
また、ツールを簡単なHello worldが作りやすいかで評価するのは、実際にそういうアプリを作る仕事の場合にしか有用ではない
SSRやその派生が話題に出ると、いつも不必要に複雑さを増しているだけではないかと自問してしまう
Reactのサーバーレンダリングコンポーネントは行き過ぎのように聞こえるし、自然な開発者体験の流れに反している
アプリケーションの複雑さが倍になり、コーディング上の落とし穴が増えて開発者が遅くなり混乱する一方で、得られるものが小さな性能向上だけなら、それだけの価値があるのか疑問だ
昔ながらのPHPサイトや、シングルページアプリではないRailsアプリも長い間うまく動いてきた
Next.jsと新しい
appディレクトリ構造で新しいアプリケーションを作っていて経験したことがある第一に、何がサーバーで起きていて何がクライアントで起きているのかを推論しにくい
知るには調べる必要があるが、コードを素早く書いているときはたいていあまり気にしなくなる
小さな変更ひとつで、ページの大きな部分が突然サーバーからクライアントへ移ろうとすることが起きやすい
最終リリース前にページごとに慎重で時間のかかる検証をしなければならないと分かっていて、それが嫌だ
第二に、既存のReactライブラリのかなりの部分はフックを使っているため、クライアントで実行されると仮定されている
そのせいでコードがクライアント側へ引っ張られることがある
新しいパラダイムと格闘する目的は、高速な読み込みと検索エンジン最適化のためのサーバーサイドレンダリングなのに、取り込んだライブラリがうまく協調してくれなければ完全に無駄になる
第三に、新しいNext.jsの
appディレクトリパラダイムにはバグがあるまだ新しく非常に複雑なので、動的ルートと並列ルート、そしてその相互作用が完全に壊れることがある
自分でNext.jsのGitHubにIssueを立てたところ、「自分も同じだ」というコメントがたくさん付いた
私が使っていたアプローチのひとつは最近Vercelの開発者が修正したが、すでに回避のために別のアプローチを選んだ後だった
いちばん腹立たしいのは、開発環境が遅延読み込みとキャッシュの魔法を使うことだ
ページ差分を計算してWebSocketのようなもので部分更新を送ろうとしているようだが、完全に壊れて復旧不能な状態になることがある
ときどき再コンパイルがサーバーからクライアントへの何らかの通信を引き起こし、Chromeのタブに戻るとタブが完全に固まって、Chromeのタスクマネージャーでプロセスを殺さなければならない
全体として、まだ非常に新しく、荒削りな部分が多い段階だ
多くのフックはサーバー側でも動作し、実際には値を初期化する以外には何もしない
PHP と JavaScript が実質的にかなり近い構文だったという点が面白い
違いは
$記号やvarキーワード程度だったのに、NodeJS は「私たちはサーバーで JS を実行したい」と言ったそして 15 年後、JavaScript が結局追いついて、実質的に PHP に似たものになったが、略語はより多く、学習曲線はより急になった
もちろん Suspense でサーバーからクライアントコンポーネントへデータをストリーミングするのは素晴らしい
NextJS 13 を使っているが、SSR を PHP で昔から可能だったように簡単にしてくれる点がよく、強くおすすめする
人々が「悪い設計のフラクタル」と呼んだとき、それは 100% そう言われても仕方がなく、問題を解決するために別の場所を探したのも妥当だった
今日の PHP ははるかに良い言語で、再検討する価値があるが、昔から今ほど良かったかのように振る舞うべきではない
そして React エコシステムを基準に NodeJS を判断すべきではない
React ベースのシステムを動かすために必要な API とラッパーの膨大な量は React コミュニティの責任だ
典型的なストックホルム症候群だ
多くの現代的な進歩が可能になったのも、まずブラウザが良くなったからだ
完全に一周したというよりは、ものすごく遠くから見ると円に見える塊に近い
一方では、並行性モデルのおかげでより高速なバックエンドを作れるようになったという点で革新的だったが、Java や PHP のような既存のバックエンド言語にあった多くの機能が欠けており、そのため多くのパターンが再発明された
言語自体も、Java がすでに持っていて PHP が向かっていた「安全性」の水準に到達するまでに何年もかかった
XML と、それが提供し得た契約上の保証のような、検証済みで標準化された技術も、重いという理由や、JSON が人間にとって読み書きしやすいという理由などで捨てられた
XML を捨てたことで、多くの時間と労力を失ったと感じている
REST/JSON API のドキュメント化はいまだに苦痛だ
20〜25 年前には、XML ペイロードからデータモデルとパーサをすでに生成できていた
XML の何がそこまで問題だったのか、今でも分からない
回線上では JSON より少し重かったが、圧縮を使うか EXI(https://www.w3.org/TR/exi/) でバイナリプロトコルに変えれば解決できる問題だった
EXI が実際に定着したのかは分からないが、当時 XML がどれほど大量に行き交っていたかを知っていたので、かなり期待していた
当時はノンブロッキング I/Oが大きな性能向上をもたらした
ところが現代の開発者の頭の中では、JSON を吐くかツールチェーンをホストするだけの間抜けなツール程度に追いやられているように見える
慣れているものを使っているのだろうが、ドキュメントサイトに、キャッシュ機能のある既製の静的サイトジェネレータや CMS の代わりに React を使うのは無駄に見える
開発者の立場では React のほうが楽しいのかもしれない
Stripe は、アカウントの API キーが入ったコード片を表示して、すぐにテストできるようにする流れを始めた
フロントエンドのドキュメントサイトには、ほとんどの場合、ドキュメント内で直接触れる実行可能な例が入っている
新しいプロジェクトのブートストラップは非常に簡単で速く、文字どおり純粋な HTML プロジェクトを始めるより簡単だ
自分は何を見落としているのだろうか?
ついでに、低評価を押す人たちの視点が本当に気になる
静的コンテンツなのに、なぜ検索ボックス用の JavaScript を少し付けた HTML として生成しないのか分からない
React を使う理由は、フロントエンド全体で1つの言語を使うためだ
ある人はあるサイトで React を使い、別の人は Gatsby/Hugo を使う、といった分断を避けられる
Next.JS は Gatsby/Hugo と同じことができ、機能はより多く、React ベースだ
サーバーがすべてをレンダリングし、CSS と Javascript はレンダリング済みページを補強するために使われていた時代を覚えているくらいには年を取っている
Web はあまりにも暗く、過剰設計な場所になってしまった
ほとんど信じがたいほどだ
だから私のアプリ制作のやり方は、まずサーバーレンダリングを行い、あとから補強する方向だ
折りたたみ可能なドロップダウンや jQuery のドラッグ&ドロップは、あれば便利な機能ではあるが、js/jQuery 時代の状態管理地獄も鮮明に覚えているので、そこへ戻りたいとは思わない
冗談はさておき、最初の Geocities ページは訪問者カウンターや marquee くらいしかない HTML だった
学校で作った最初の PHP アプリケーション/プロジェクトでもまだ JS は使っておらず、静的メニューとヘッダーにはフレームを使い、データは単にフォーム送信でバックエンドに送っていた
そういう時代があった
大学のカリキュラムに含まれていた最初の1年間のインターンでは、Java バックエンド、プレゼンテーション層の JSX テンプレート、そしてダイアログやアニメーションするアコーディオンのようなものに PrototypeJS を使っていた
当時のアニメーションとは「この要素の高さを1秒間に何回か変える」ことだった
最初の職場では、カートに追加、画像カルーセルといった形でページを補強する JS をたくさん書いており、それが jQuery の時代だった
次の職場では、カスタマーサポート担当者が SAP のようなものをのぞき込む UI を BackboneJS でひどい出来に作った
その次の仕事も、顧客向け投資銀行フロントエンドを BackboneJS で作り直すことだった
それは当時言われていたシングルページアプリケーションに非常によく合うユースケースだった
検索エンジン最適化は不要で、純粋なフロントエンドレンダリングで十分速く、API 中心であり、当時は Web とモバイルに同じ API を使えることに人々が気づき始めていた時期だった
私の考えでは、Web 開発ツールが今ほど良かったことはなく、ユーザー体験も何年にもわたって大きく改善されてきた
私たちが AJAX と呼んでいたものは、気の利いた追加のおもちゃから、クライアントコンポーネントやシングルページアプリという形の日常的な基本要素へと成長した
サーバーは望むなら今でも強力だが、ダッシュボード、地図、ゲーム、フォーラム、オフィスアプリ、オンライン IDE のようなインタラクティブなアプリでは、強力なクライアント側の能力が役に立つ
これによって、日常的なアプリは各 OS 専用のカスタムデスクトップアプリから、すべてのノート PC とデスクトップをまたぐ汎用プラットフォームへ大規模に移行できた
もちろん、その力にはより多くの複雑さが必要だった
HTML/CSS でブログやランディングページを書くことと、完全な Web アプリを書くことは大きく違う
Angular と React は、当時サーバーサイド言語に比べて JS ランタイムと言語自体がはるかに原始的だった時代に、以前より何倍も複雑なアプリ開発を助けるために作られた
2010年代後半には、複数の JS フレームワークが問題のごく小さな一部ずつしか解決していなかった、本当に苦痛な時期があった
最近はましになった
Next が勝ち、デフォルトになったし、それには理由がある
中程度の複雑さのアプリに適切な抽象化レベルを提供し、サーバーサイドレンダリングとクライアントサイドページをうまく混ぜられるようにしてくれる
React サーバーコンポーネントは、その区別をよりきれいな第一級の概念にしてくれる
ただし、それは一定以上の複雑さがあって初めて意味を持つ
必要なければ使わなければよい
ほとんど静的なブログやドキュメントサイトなら、もっと単純なアーキテクチャがある
今でも HTML を書き、必要な分だけ JS を数行振りかけることができるし、たいていの小規模ビジネスには WordPress や Wix も使える
しかし、より複雑なアプリを作るなら、些細なインタラクションのたびにサーバーと往復して UI を再計算し、HTML ページ全体を毎回送っていたやり方に比べて、React は本当に夢のようだ
そのような方式では、コンテキストやページ上の位置、途中まで入力したフォームなどを失わせ、フォームデータを状態として使うことを助長し、うっかり戻るを押したり、手軽なクラウドスケーリングが登場する前の頻繁なサーバー障害で作業を失ったりすることが多かった
私の考えでは、誤って適用されたときだけ過剰設計なのだ
適切なユースケースでは、こうしたツールは本当に有用で、ときには不可欠でもある
残念なのは、必要ない、あるいはむしろ有害な状況でも、あまりに多く教えられ、利用が奨励されている点かもしれない
結局は作業に合った道具を使うべきだ
React を Vue、Svelte、HTMX より推したいわけではなく、クライアント側の複雑さにも使い道があるという意味だ
React は、よりモダンで簡単、速く、安価な代替手段に追いつこうと苦労しているように見える
しかし、根本的な問題である再レンダリング、頻繁に必要になるメモ化、漏れのある抽象化を直す代わりに、React はさらに複雑になっている
最終的な結果が素晴らしいならその努力も理解できるが、そうではない
React は実環境ではベンチマークが示すより遅く、Next はさらにひどい
最近、Next で作られた非常に遅い Web サイトをたくさん見た
本当に理解できない
React チームが React を改善したいなら、核心を直すべきだ
今ではエコシステムの依存関係が多すぎて壊れてしまう
Target.com、Walmart.com、Microsoft Teams、そして数え切れないほど多くのサイトが React だ
巨大なコンポーネントエコシステムと、その上に築かれた企業もある
核心となる概念は壊れているが、直すということは残りのすべてを壊しかねないという意味だ
どうせ全部壊すなら、別のものを使うほうがよい
今は依存関係の質量のせいで React に縛られたまま転がり続けるしかない
React は再レンダリングをデフォルトにして抜ける方式である一方、Vue、Solid、Preact、Svelte はすべて必要なところに入る方式だ
これが、正しく使うのが難しく、特定の種類のバグに弱い核心的な理由の一つだ
表面上は普通の JavaScript のように見えても、常に抜けるべきかを気にしなければならず、他のフレームワークではそうしたバグはまれ、あるいはほとんどない
https://react.dev/blog/2023/03/22/react-labs-what-we-have-be...
なぜ今になってバックエンドで React を使って HTML をレンダリングするのか理解できない
10年前に戻ろうということなのか
最近 Svelte と Django テンプレートを頻繁に行き来しているが、Svelte は DOM を理解しているというだけで体験がずっと良い
JS ではないテンプレートシステムでこういうものを見たことがない
PHP や jQuery なども経験してきた
さらにフロントエンドが React なら、すべての HTML 生成を一か所に置くのがずっと簡単になる
そこに静的型付けのサポートもある
RSC や React が史上最高の解決策だと擁護するつもりはないが、ここにある反論の一部は未熟だ
React/RSC の利点は、サーバーが HTML/CSS と少しの JavaScript を返すことと技術的に同じではない
依然として一つのアプリであり、SSR/ハイドレーションと比べてクライアント/サーバー境界をより賢く扱う方法だ
React が自ら行き止まりへ向かうように設計したのか、脱出経路は何なのかについて、もっと詳しい反論ならいくらでも読みたいが、PHP に戻ることは答えではない
React はそれほど学ぶのが難しいものでもなく、非開発者でもブートキャンプ数週間で基礎を身につける理由がある
JSX は Django、PHP、Rails のテンプレートシステムより客観的に優れている
雑に投げられている反論の半分は、Lighthouse のようなもので自分のプロジェクトをベンチマークしたことすらなさそうだ