マニュアルモード
デフォルトでは、remix dev
はオートマチック車のように動作します。
アプリのコードでファイルの変更が検出されるたびに、アプリサーバーを自動的に再起動することで、アプリサーバーを最新のコード変更に追従させます。
これは、邪魔にならないシンプルなアプローチであり、ほとんどのアプリでうまく機能すると考えています。
しかし、アプリサーバーの再起動が遅く感じられる場合は、ハンドルを握って remix dev
をマニュアル車のように操作できます。
つまり、ギアチェンジのためにクラッチの使い方を学ぶ必要があります。 また、慣れるまではエンストするかもしれません。 習得には少し時間がかかり、メンテナンスするコードも増えます。
大いなる力には、大いなる責任が伴います。
デフォルトのオートマチックモードで何らかの不便さを感じない限り、それだけの価値はないと考えています。 しかし、もしそうであれば、Remix が対応します。
remix dev
のメンタルモデル
ドラッグレースを始める前に、Remix が内部でどのように動作するかを理解しておくと役立ちます。
特に、remix dev
が 1つではなく、2つのプロセス を起動することを理解することが重要です。それは、Remix コンパイラとあなたのアプリサーバーです。
詳細については、私たちのビデオ "新しい開発フローのメンタルモデル 🧠" をご覧ください。
以前は、Remix コンパイラを「新しい開発サーバー」または「v2 開発サーバー」と呼んでいました。
技術的には、remix dev
は、ホットアップデートを調整するための単一のエンドポイント (/ping
) を持つ小さなサーバーを含む Remix コンパイラの薄いレイヤーです。
しかし、remix dev
を「開発サーバー」と考えるのは役に立たず、開発時にアプリサーバーを置き換えているという誤った印象を与えます。
remix dev
はアプリサーバーを置き換えるのではなく、Remix コンパイラ と並行して アプリサーバーを実行するため、両方の長所が得られます。
- Remix コンパイラによって管理されるホットアップデート
- アプリサーバー内で開発中に実行される実際のプロダクションコードパス
remix-serve
Remixアプリサーバー (remix-serve
) は、マニュアルモードをすぐにサポートしています。
-c
フラグなしで remix dev
を実行している場合、暗黙的に remix-serve
をアプリサーバーとして使用しています。
remix-serve
には、高回転時に自動的にギアをよりアグレッシブにシフトするスポーツモードが組み込まれているため、マニュアル運転を学ぶ必要はありません。
さて、この車の比喩は少し無理があるかもしれません。😅
言い換えれば、remix-serve
は、自身を再起動することなく、サーバーコードの変更を再インポートする方法を知っています。
ただし、-c
を使用して独自のアプリサーバーを実行している場合は、読み進めてください。
マニュアル運転を学ぶ
--manual
でマニュアルモードをオンにすると、いくつかの新しい責任を負うことになります。
- サーバーコードの変更が利用可能になったことを検出する
- アプリケーションサーバーを稼働させたままコードの変更を再インポートする
- これらの変更が取り込まれた後に、Remixコンパイラーに「準備完了」メッセージを送信する
コードの変更を再インポートすることは、JSのインポートがキャッシュされるため、難しいことがわかります。
コードの変更でモジュールを再インポートしたい場合は、インポートキャッシュを破棄する方法が必要です。
また、モジュールのインポートはCommonJS (require
) とESM (import
) で異なるため、さらに複雑になります。
server.ts
を実行するために tsx
または ts-node
を使用している場合、これらのツールはESM TypescriptコードをCJS Javascriptコードにトランスパイルしている可能性があります。
この場合、サーバーコードの残りの部分が import
を使用していても、server.ts
でCJSキャッシュの破棄を使用する必要があります。
ここで重要なのは、サーバーコードがどのように実行されるかであり、どのように記述されるかではありません。
require
キャッシュの破棄
1.a CJS: CommonJS はインポートに require
を使用し、require
キャッシュへの直接アクセスを提供します。これにより、リビルドが発生したときに サーバーコードのみ のキャッシュを破棄できます。
たとえば、Remix サーバービルドの require
キャッシュを破棄する方法は次のとおりです。
require
キャッシュのキーは 絶対パス であるため、サーバービルドのパスを絶対パスに解決するようにしてください!
import
キャッシュの破棄
1.b ESM: CJS とは異なり、ESM ではインポートキャッシュに直接アクセスすることはできません。 この問題を回避するために、タイムスタンプのクエリパラメータを使用して、ESM にインポートを新しいモジュールとして扱うように強制することができます。
ESM では、import
キャッシュからエントリを削除する方法はありません。
タイムスタンプによる回避策は有効ですが、import
キャッシュが時間とともに増大し、最終的にメモリ不足エラーを引き起こす可能性があります。
この問題が発生した場合は、remix dev
を再起動して、新しいインポートキャッシュで再開できます。
将来的には、Remix はインポートキャッシュを小さく保つために、依存関係を事前にバンドルする可能性があります。
2. サーバーコードの変更を検出する
CJSまたはESMのインポートキャッシュを破棄する方法ができたので、それを利用してアプリサーバー内でサーバービルドを動的に更新する時が来ました。 サーバーコードの変更を検出するには、chokidarのようなファイルウォッチャーを使用できます。
3. "ready" メッセージの送信
ここで、アプリサーバーが最初に起動したときに、Remixコンパイラに "ready" メッセージを送信していることを再確認する良い機会です。
手動モードでは、サーバービルドを再インポートするたびに "ready" メッセージを送信する必要もあります。
4. 開発対応リクエストハンドラー
最後のステップは、これらすべてを開発モードのリクエストハンドラーにまとめることです。
素晴らしい! 開発モードで実行するときに、新しいマニュアルトランスミッションをプラグインしましょう。
完全なアプリサーバーコードの例については、テンプレートまたはコミュニティの例を確認してください。
リビルドをまたいでインメモリのサーバー状態を保持する
サーバーコードが再インポートされると、サーバー側のインメモリ状態はすべて失われます。 これには、データベース接続、キャッシュ、インメモリデータ構造などが含まれます。
リビルドをまたいで保持したいインメモリの値を記憶するユーティリティを以下に示します。
たとえば、リビルドをまたいで Prisma クライアントを再利用するには、次のようにします。
また、remember
ユーティリティ も便利で、こちらを使用することもできます。