
7,007 文字

私はフレームワークを作ることについて考えていました。Wakuのようなものを見ると、とてもワクワクします。ミニマルなサーバーコンポーネントフレームワークというアイデアは素晴らしいです。そしてNextにはイライラすることがたくさんあります。最大の問題は、長年DXに苦労した後、Nextは現在本当に良いDXを持っていますが、そこに至るまでの10年間の傷跡がますます目立つようになっていることです。クラックを塞ごうとする試みに気づくことがどれだけ多いか不思議です。過去に構築したものを元に戻さなければならないからです。
私が欲しいのは、現在のNextの優れた点を、技術的負債なしに取り入れたものです。今日のスポンサーは、Reactアプリのバックエンドをはるかに簡単に管理できるようにします。詳細をすべて説明するよりも、Convexで作業することがどれほど素晴らしいかを示したいと思います。
少し時間をかけて作った小さなアプリから始めます。T3チャットスタイルの体験をテストする方法として作りました。そのもう一方に何か気づきましたか?これはConvexプラットフォームであり、Convexプラットフォーム上ではデータが発生するとすぐに表示されます。すべてを削除したときに何が起こるかを見てください。常に同期が取れています。
これがConvexの魔法です。正しい時間に正しい場所でデータが更新されるかどうか心配する必要はありません。一部のユーザーのデバイスで古いデータが表示されることを心配する必要もありません。クレイジーで奇妙なフェッチやポーリング作業も必要ありません。クライアントでデータを要求するだけです。ここにあるクライアント用のコードは実際に驚くほど単純です。APIメッセージのリスト、メッセージ送信用のEdクエリがあり、それからメッセージをレンダリングするだけです。
しかし、何か引っ掛けがあるはずですよね?バックエンドは本当に複雑なのでしょうか?見てみましょう。APIメッセージリストをコマンドクリックすると、同じプロジェクト内のconvexフォルダにあるバックエンドコードに移動します。これは自動的にConvexによってデプロイされます。ここには、DBからメッセージを収集して返すクエリがあります。では、送信は本当に複雑なのでしょうか?全然そんなことはありません。
まず、本文と作成者を新しいメッセージとして挿入します。その後、すべてのメッセージをリストアップし、最後の10個を取得して、アクションに送信します。アクションは、迅速な更新の外で実行されるものです。他のソースが必要なもの、APIを呼び出すもの、複数回変更するものなどは、アクションを通じて行う必要があります。これらのメッセージを作成した直後にアクションをスケジュールし、このプレースホルダーメッセージのIDを送信します。そして、これはすべて型安全でTypeScriptなので、コマンドクリックするだけでアクションを確認できます。
ここでは、OpenAIに送信するデータを生成していますが、この場合はGeminiに送信しています。同じようなものです。テキストをストリーミングし、すべて送信し、新しいチャンクが入るたびに、CTX run mutationでバックエンド上のデータを最新の状態に保ちます。
では、クライアントをどのように更新するのでしょうか?データがデータベースに存在する限り、ユーザーは正しいデータを見ています。それらの関係を管理しようとする奇妙さはもうありません。人々を同期させようとする混乱もありません。シンプルなCRアプリから大規模な共同作業型Figmaクローンまで、構築したいものはすべて、Convexのようなアーキテクチャでかなり簡単になります。
近いうちにもっと多くのものをConvexに移行し始められることを願っています。今日の動画のスポンサーであるConvexに感謝します。so.l/cconvexで今すぐチェックしてください。
しかし、私がより考えれば考えるほど気づいたことが2つあります。第一に、私は文字通りNextを再構築するだけでしょう。私が変更することはそれほど多くないでしょう。おそらく設定ベースのルーティングが好きなので、設定ルーティングオプションを追加するでしょう。私はマジックエクスポートが嫌いなので、そのほとんどを取り除くでしょう。設定は1つだけです。それだけです。それは主に、Nextの良いところをどれだけ最小限に再現できるかというクリーンアップのようなものになるでしょう。
ReactにPreactがしたことをNextJSに対して行うようなものですが、それは素晴らしいことのように聞こえますが、私が何を作りたいかを固めたと思うたびに、次に何か驚くべきことが起こり、最初から構築するとしたら根本的に変わっていたであろう方法に影響を与えるのです。最近の大きな例はdynamic IOでした。dynamic IOは、NextJSでキャッシュと部品の再利用方法に大きな変化をもたらしました。
以前は、要素が動的か静的かを推測しようとする狂ったコンパイラハックと、それらすべてを調整する奇妙なマジックエクスポート(これらすべてを私が構築しなければならなかったでしょう)がありましたが、今では動的コードの非同期的な性質を使用して、要素が動的であるべきかどうかを識別しています。
多くの奇妙なコンパイラハックの代わりに、今はReactのレンダリングプロセスを実行し、出力を取得してから、非同期部分が応答する前にそれらを殺す単一のランタイムハックがあります。これがキーポイントで、ここでのdouble set immediateを使用すると、非同期キュー全体をスキップして、同期部分で簡単に抜け出すことができます。
もしこのことを知る前にフレームワークを構築していたら、このハックのような品質の体験に近づけるために、すべてに対して狂ったコンパイラを構築しようとしていたでしょう。このハックとその発見により、このフレームワークを構築する概念全体がはるかに簡単になり、結果として得られるDXもはるかに良くなります。
だから、これを見て「今がその時だ、必要なすべての部品がある」と思うかもしれません。しかし、実際には逆の影響を与えています。私は1年前も同じように感じていました。もし1年前に作業を開始していたら、今頃はv0版がほぼ完成していたでしょう。そして、これを見て、やったことの半分を捨てるか、完全に考え直して別の方向に何か別のものを構築しなければならなかったでしょう。そうすると、すぐにNextで問題を抱えている同じ場所に行き着くでしょう。正しい解決策を見つけるまで、技術的負債に技術的負債を重ねるだけになるでしょう。
私たちは完璧ではないにしても、非常に近づいていると思います。これらの変更の規模は毎年小さくなっています。2021年には、サーバーコンポーネントとアプリルーターが登場し、それらは私たちの操作方法に巨大で記念碑的な変化をもたらしました。荒い部分がたくさんありましたが、それ以来多くの部分を改善してきました。
翌年にはサーバーアクションが登場しました。これはさらに粗削りでしたが、奇妙な落とし穴がたくさんあり、私はまだよく遭遇します。
面白いものを1つ紹介します。おそらく知らなかったでしょう。one out fiveスタックの動画でこれを学びました。サーバーアクション内でrevalidate pathを呼び出すと、そのパス内のすべてのキャッシュエンティティが削除されます。例えば、私の場合、「2つのランダムポケモンを取得する」という巨大なキャッシュエンティティがあります。なぜなら、「すべてのポケモンを取得する」を呼び出す必要があり、これは永久にキャッシュしたいからです。
revalidate pageを呼び出すとキャッシュが削除されますが、cookie.setを呼び出してもそうはなりません。パフォーマンスを向上させたい場合は、代わりにこのようにすることができます。cookiesの呼び出しを取得します(ちなみに、なぜcookiesが非同期なのかというと、それが非同期であるため動的であることを知るためのdynamicの仕組みのためです)。
これを行い、jar.[何もない]にJSON.stringify(Math.random())を設定すると、revalidateからcookieの更新に変更したため、大幅に高速な体験になります。何も設定しなければ、ブラウザに更新を指示するものや更新を送信するものが何もないため、何も起こりません。
これをここに置くことで、ボタンをクリックすると、サーバーは更新されたHTMLを生成し、JavaScriptが引き継いで正しくレンダリングできるようにレスポンスとして送信します。しかし、revalidate pathオプションを使用すると、はるかに遅くなります。
この動作はどこにも文書化されていません。チームと話し合わなければなりませんでしたが、彼らは間違っていました。確認すると「あなたが正しい、それをドキュメントに入れるべきだ」と言いました。これらの動作が忍び込んでいる量は、誰かが動揺する理由を理解するのに十分です。
とはいえ、これらは私のような狂人がダイブするパフォーマンスに関するエッジケースです。これらはすべて時間とともに改善されるでしょう。これらの新しいものを早い段階で使用している私としては、新しいDynamic IOやcacheを使用していることを考えると、これらの動作が機能していることに驚いています。まだ完全に文書化されていないのは妥当ですが、これほど本質的に複雑な環境を構築する際にこれらの動作がどれだけ必要かを示しています。
私は自分の少し単純なものを構築することができますが、完成するまでに、追加したいと思う5つ以上のものが発生し、今や全く同じ複雑さの問題を抱えていますが、それを構築するための時間とインセンティブははるかに少なくなっています。
これをどう表現するかはいくつかの方法があります。一方では、私がNextJSを非常に気に入っていて、彼らが正しいことをすると信頼しているので、自分のフレームワークを作りたくないと言えるかもしれません。もう一方では、物事があまりにも速く変化していて、追いつこうとすると発狂して死んでしまうと言えるかもしれません。
現実はその2つの間のどこかにあり、私は現実はもっと興奮していると言えます。私はNextチームとReactチームが考え出し続けている新しいものや新しいアイデア、そしてエコシステムとして私たちが得る利益を愛しています。すべてのフレームワークがこれらすべてを採用するとは思いません。
しかし、Reactエコシステムの外でさえ、多くのものが採用されていることに驚いています。Elixirのチームはデータクエリーがくるまえにシェルをストリーミングできるように、ストリーミングの利点を採用しようとしていることを知っています。Vueの世界の人々もサーバーオンリーコンポーネントとそれらの間のオーケストレーションを活用する方法を考えていることを知っています。これらの勝利を生かし、他の場所にもたらす作業が進んでいることを知っています。それは本当にエキサイティングです。
それはとても興奮することなので、私はただ座って見ているだけにしたいと思います。輪の中に飛び込むのは楽しいですが、それは新しいものへの私の興奮を奪うでしょう。そして、それが最も恐れていることです。
新しいReactのリリースに苛立つ人をたくさん知っています。なぜなら、それは本質的に彼らにとってより多くの作業を意味するからです。彼らのフレームワークに新機能を追加する必要があることを意味します。Reactを中心に構築された彼らのプラグインやライブラリには、新しいエッジケースやバグに対処する必要があるかもしれません。
もし私がこれらのことの周りと深く関わっている人々の一人だったら、これらの新しいものが出てきたときにはるかにストレスが多くなるでしょう。その結果、私はあまり興奮しなくなるかもしれません。そして、それは私が変えたくないことです。
私はこれらの新しいことに対する興奮を維持したいと思います。なぜなら、それらは本当にエキサイティングだからです。このエコシステムではとても多くの素晴らしいことが起こっていて、今日のそれを再実装して明日のそれを無視したくはありません。私はただそれを楽しみ、次に何が起こるかを見たいだけです。そして、それが私がフレームワークを構築していない理由です。
それは魅力的です。今それがどのようなものになるか大まかにわかります。しかし、それは単に機能が常に6ヶ月遅れているミニマルなNextJSになるでしょう。そして、それを構築したくはありません。私は最高のものを構築し、構築するものに最高のものを使いたいと思います。そして、良くも悪くも、現時点ではそれは絶対にNextJSです。
それは機能性、サポート、そして素晴らしい新機能のバランスが取れており、ユーザーにとって他の場所で再現するのが本当に難しい体験につながります。
例えば、このTurboバージョンのコードを実際に見てみましょう。元の動画を見ていない場合、これがそのNext RSCバージョンです。Turboバージョンに移動して、ボタンをただハンマーのように叩くと、これは開発サーバーでもそれほど速いです。信じられないかもしれませんが、本番環境ではさらに速くなります。
このバージョンがこれほど高速な理由は、いくつかのハックを忍び込ませたからです。「いくつか」というより、実際には1つだけです。現在のポケモンをランダムペアとしてレンダリングするか、クッキーがある場合は、持っている2つのポケモンのクッキーを使用します。また、次のペアを2つの異なるランダムポケモンとして定義します。
実際にボタンをクリックして一方に投票すると、バトルを記録し、クッキーを次のペアに更新します。ここでは特別なことは何もしていません。値を下に渡したり、ブラウザを呼び出して「これはHTMLに埋め込む必要があるデータだ」と言ったりしていません。これらのステップを踏んでこのデータを取得し、このアクションでクッキーとして使用できるようにはしていません。
私はただ明白な単純なコードを書いています。このクッキーがあり、次のクッキーになってほしい値があり、それを設定するだけです。それはとても素晴らしいです。クッキーと大規模なパフォーマンス最適化を扱うのがこれほど優雅だったことはありません。以前はこれにはもっと作業が必要でした。
「revalidate path」をオンに戻すと、その巨大なキャッシュの更新が行われ、はるかに時間がかかります。しかし、それをコメントアウトしてこれを更新すると、今度は即時になります。パフォーマンスの違いを感じる理由は、ここに上にある隠れたdivが次の次の2つのポケモンの画像をレンダリングしているからです。
その結果、コンソールに移動してネットワークタブを見ると、番号730と352をフェッチしただけです。これらは現在の2つではありませんが、投票すると730と352が次に来ます。なぜなら、ブラウザに次が何かという意味ではなく、HTMLに次のものがあるので、必要なデータを取得できるからです。そして、サーバーはHTMLを更新するだけです。
このソリューションがいかに単純で優雅であるかは面白いですね。このソリューションがこれほど単純で優雅である理由は、Nextがほとんどのプリミティブを解決したからです。他のすべてのフレームワークでは、これを行うためにより多くの労力が必要です。
パフォーマンス最適化を得るためのさまざまな方法があり、Elixirバージョンを高速化することができましたが、それははるかに複雑で、パフォーマンスをそれほど良くするために多くの異なることをする必要がありました。また、二重サーバーレンダリングの動作を回避する必要がありましたが、ここではその必要はありません。ここには回避策はありません。これは単に私がやろうとしていることを記述する単純で焦点を絞ったコードです。
だから私は興奮しています。私のコードがフレームワーク固有に見えないようにしたいです。私のコードが何が起こっているかを正確に記述し、動作が期待通りに動くようにしたいです。フレームワークを学ぶ必要が少なく、単にJavaScriptを知っていればよいほど、より良いです。
そのため、私はNextに興奮しており、また、この動画をまとめると、それが私がフレームワークを構築していない理由です。
これは私が期待していたよりもよく仕上がったと思います。どのチャンネルに載るかわかりません。この動画が良かったと思うなら知らせてください。私が普段やっているものとはかなり違いますから。次回まで、どうぞフレームワークを壊さないでください。


コメント