ホームページ  >  記事  >  ウェブフロントエンド  >  サーバーアクションが修正されました

サーバーアクションが修正されました

Linda Hamilton
Linda Hamiltonオリジナル
2024-10-21 06:22:02672ブラウズ

サーバー アクションは、クライアント コードを削減し、サーバーとの通信を必要とする対話を簡素化するアイデアとして登場しました。これは、開発者が記述するコードの量を減らすことができる優れたソリューションです。ただし、他のフレームワークでの実装にはいくつかの課題があり、それを見逃してはなりません。

この記事では、これらの問題と、Brisa がどのように解決策を見つけたかについて説明します。

サーバーアクションが必要な理由は何ですか?

サーバー アクションが提供するものを理解するには、サーバーとの通信が以前どのように行われていたかを確認することが役立ちます。おそらく、サーバーとのやり取りごとに次のアクションを実行することに慣れているでしょう:

  1. ブラウザ イベントをキャプチャします (クライアント)
  2. データの正規化とシリアル化 (クライアント)
  3. サーバーにリクエストを送信します(クライアント)
  4. エンドポイント API でリクエストを処理します (サーバー)
  5. 必要なデータを応答します(サーバー)
  6. サーバーからの応答を待ち、それを処理します(クライアント)
  7. クライアント上のデータを更新し、変更をレンダリングします(クライアント)

これらの 7 つのアクションは、インタラクションごとに繰り返されます。たとえば、10 の異なるインタラクションを含むページがある場合、リクエストの種類、URL、送信されたデータ、顧客のステータスなどの詳細のみを変更して、非常に似たコードを 10 回繰り返します。

よく知られた例は次のとおりです
a:

<input
  onInput={(e) => {
    // debounce
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      fetch("/api/search", {
        method: "POST",
        body: JSON.stringify({ query: e.target.value }),
      })
        .then((res) => res.json())
        .then((data) => {
          setState({ data });
        });
    }, 300);
  }}
/>

そしてサーバー内:

app.post("/api/search", async (req, res) => {
  const { query } = req.body;
  const data = await search(query);
  res.json(data);
});

クライアント バンドル サイズの増加…開発者のフラストレーション。

Server Actions have been fixed


イライラした開発者

サーバーアクションの仕組み

サーバー アクションは、これらのアクションを リモート プロシージャ コール (RPC)カプセル化します。これにより、クライアントとサーバーの通信が管理され、クライアント上のコードが削減され、サーバー上のロジックが集中化されます。 :

  1. ブラウザ イベントをキャプチャします (RPC クライアント)
  2. データの正規化とシリアル化 (RPC クライアント)
  3. RPC サーバーにリクエストを送信します (RPC クライアント)
  4. データを使用してサーバー上でアクションを実行します(RPC サーバー)
  5. オプション 1:
  • サーバーからレンダリングし、クライアントにストリーミングを送信します(RPC サーバー)
  • 変更が表示されるようにストリームのチャンクを処理します(RPC クライアント)
  1. オプション 2:
  • 必要なデータを返信し、プロパティをサーバー ストアからクライアント ストアに転送します(RPC サーバー)
  • 変更をリッスンしていたシグナルをストア内の変更に反応させます(RPC クライアント)

ここでは、Brisa RPC によってすべてが行われます。

Server Actions have been fixed


リモート プロシージャ コール

これは サーバーコンポーネント からのコードになります:

<input
  onInput={(e) => {
    // debounce
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      fetch("/api/search", {
        method: "POST",
        body: JSON.stringify({ query: e.target.value }),
      })
        .then((res) => res.json())
        .then((data) => {
          setState({ data });
        });
    }, 300);
  }}
/>

ここでは、クライアント コードはサーバー コンポーネントであるため、開発者は作成しません。 onInput イベントはデバウンス後に受信され、クライアント RPC によって処理されます。一方、サーバー RPC は「アクション シグナル」を使用して、そのストア プロパティに登録されたシグナルを持つ Web コンポーネントをトリガーします。

ご覧のとおり、これによりサーバー コードが大幅に削減され、何よりも、対話のたびにクライアント上のコード サイズが増加しません。 RPC クライアント コードは、そのような対話が 10 回でも 1000 回でも、固定の 2 KB を占有します。これは、クライアント バンドル サイズが 0 バイト増加しても、増加しないことを意味します。

Server Actions have been fixed


クライアント バンドル サイズが 0 バイト

さらに、再レンダリングが必要な場合、これはサーバー上で行われ、HTML ストリーミングで返されるため、ユーザーは、後でクライアント上でこの作業を行う必要があった従来の方法よりもはるかに早く変更を確認できます。サーバーの応答。

このようにして:

  • ユーザーエクスペリエンス (UX)改善
  • 開発エクスペリエンス (DX)改善

Server Actions have been fixed


開発者様、おめでとうございます

Brisa Server Actions と他のフレームワークの違い

1. キャプチャするイベントの数

React などの他のフレームワークでは、イベントではなく、フォーム onSubmit の一部であるアクションのみに焦点を当ててきました。

クライアント コードを追加せずにサーバー コンポーネントから処理する必要がある非フォーム イベントが多数あるため、これは問題です。たとえば、自動提案を実行する入力の onInput無限スクロールをロードする onScrollonMouseOver ホバーなどを実行します

Server Actions have been fixed


アプリケーションは予想よりもインタラクティブです

2. サーバー アクションに対する HTML コントロールの追加

多くのフレームワークも、HTMX ライブラリをサーバー アクションの非常に異なる代替手段としてみなしていますが、実際には、HTML に追加の属性を追加するだけで、サーバー アクションと組み合わせてより多くの可能性をもたらす非常に優れたアイデアがもたらされています。 RPC クライアントは、前に見た debounceInput などを考慮に入れることができます。また、リクエストの作成中にスピナーを表示するインジケーターや、RPC クライアントでエラーを処理できるようにするなど、他の HTMX アイデアもあります。

Server Actions have been fixed


HTMX のアイデア

3. 関心事の分離

サーバー アクションが React に導入されたとき、新しいパラダイム シフトが起こり、多くの開発者はサーバー アクションを使用する際にメンタル チップを変更する必要がありました。

私たちは、Web プラットフォームにできるだけ馴染みのあるものにしたいと考えました。これにより、サーバーからシリアル化されたイベントをキャプチャし、そのプロパティを使用できるようになります。少し異なる唯一のイベントは、FormData が転送済みで e.formData プロパティを持つ onSubmit ですが、残りの イベント プロパティ は対話可能です。これはフォームをリセットするの例です:

<input
  onInput={(e) => {
    // debounce
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      fetch("/api/search", {
        method: "POST",
        body: JSON.stringify({ query: e.target.value }),
      })
        .then((res) => res.json())
        .then((data) => {
          setState({ data });
        });
    }, 300);
  }}
/>

この例にはクライアント コードがまったくなく、サーバー アクション中に CSS を使用してインジケーターで送信ボタンを無効にすることができます。これにより、フォームが 2 回送信されなくなり、同時に、サーバー上でアクションを実行し、e.formData でフォーム データにアクセスし、イベントの同じ API を使用してフォームをリセットします。

精神的には、Web プラットフォーム での作業と非常に似ています。唯一の違いは、すべてのサーバー コンポーネントのすべてのイベントがサーバー アクションであることです。

このようにして、実際の関心事の分離が行われ、「ユーザーサーバー」 または 「使用クライアント」 に入れる必要はありません。コンポーネントもう

.

すべてはサーバー上でのみ実行されるということに注意してください。唯一の例外は、クライアント上で実行されるsrc/web-componentsフォルダであり、そこではイベントは正常です

Server Actions have been fixed


2 つの異なる世界、しかし一致する

4. イベントの伝播

Brisa では、サーバー アクションは DOM イベントであるかのようにサーバー コンポーネント間で伝播されます。つまり、サーバー アクションからサーバー コンポーネントのプロパティのイベントを呼び出すことができ、親サーバー コンポーネントのサーバー アクションが実行されます。

<input
  onInput={(e) => {
    // debounce
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      fetch("/api/search", {
        method: "POST",
        body: JSON.stringify({ query: e.target.value }),
      })
        .then((res) => res.json())
        .then((data) => {
          setState({ data });
        });
    }, 300);
  }}
/>

この場合、onAfterMyAction イベントが親コンポーネントで実行され、サーバー上でアクションを実行できます。これは、複数のサーバー コンポーネントに影響を与えるアクションをサーバー上で行う場合に非常に役立ちます。

Server Actions have been fixed


アクションの伝播

4. 両方の世界間のコミュニケーション

特にここ数週間、X (旧 Twitter) でのいくつかの議論の後、Web コンポーネントは少し眉をひそめるようになりました。ただし、HTML の一部であるため、次のような理由からサーバー アクションと対話するには最良の方法です。

  1. サーバーから任意の Web コンポーネント イベントキャプチャし、クライアント/サーバー通信を生成できます。例 。これは非常に強力です。Web コンポーネント内のすべてのイベントは、サーバー ロジックを配置せずにクライアント ロジックのみであるため、Web コンポーネントを使用するときに単純にサーバーからサーバー アクションを実行できます。
  2. HTTP プロトコル は、ストリーミング でハイパーテキスト (HTML) を 転送するために設計された目的で使用できます。サーバー アクションからのレンダリングで Web コンポーネントの属性が更新されると、RPC クライアントの差分アルゴリズムにより、Web コンポーネントはそれほど手間をかけずに更新されます。 Brisa の Web コンポーネント属性は、Web コンポーネントの内部部分を再レンダリングせずに反応させるシグナルです。他のフレームワークでのこのプロセスは非常に複雑になるため、RPC サーバーは HTML ではなくネットワーク経由で JSON または JS を処理する必要があり、ストリーミングの実装がより複雑になります。

Web コンポーネントで属性を使用するには、Web コンポーネントを使用せずにサーバーからクライアントにデータを送信するのと同じ方法でシリアル化が必要です。したがって、両方を使用すると、追加のシリアル化を管理する必要はありません

注: HTML のストリーミングと差分アルゴリズムによる処理については、興味があればこの別の記事で説明しています。

Server Actions have been fixed


ネットワーク経由でストリーミング中のハイパーテキスト

5. 新しい概念: アクションシグナル

Brisa では、サーバー アクションにさらに強力なパワーを与える新しい概念を追加しました。この概念は 「アクション シグナル」 と呼ばれます。 「アクションシグナル」の考え方は、2 つのストアがあり、1 つは サーバー に、もう 1 つは クライアント にあるということです。

なぜ 2 つの店舗があるのですか?

デフォルトのサーバーストアリクエストレベルでのみ存在します。また、クライアント表示されないデータを共有することもできます。たとえば、ミドルウェアにユーザーを設定させ、任意のサーバー コンポーネント内の機密ユーザー データにアクセスできるようにすることができます。リクエストレベルで動作することにより、各リクエストには独自のストアがあり、どのデータベースにも保存されないため、異なるリクエスト間で競合が発生することは不可能になります。リクエストが完了すると、デフォルトで終了します。

一方、クライアントストアでは、消費されたときの各プロパティシグナルであるストアです。つまり、更新されると、そのシグナルをリッスンしていた Web コンポーネントが反応します。

しかし、「アクションシグナル」 の新しいコンセプトは、リクエストを超えてサーバーストアの寿命を延長できるということです。これを行うには、次のコードを使用する必要があります:

<input
  onInput={(e) => {
    // debounce
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      fetch("/api/search", {
        method: "POST",
        body: JSON.stringify({ query: e.target.value }),
      })
        .then((res) => res.json())
        .then((data) => {
          setState({ data });
        });
    }, 300);
  }}
/>

この transferToClient メソッドは、サーバー データを クライアント ストアに共有し、シグナルに変換します。この方法では、多くの場合、サーバーから再レンダリングを行う必要がなく、サーバー アクションから、そのシグナルをリッスンしていた Web コンポーネントのシグナルに反応させるだけで済みます。

このストア転送により、

サーバー ストアの寿命が決まりました:

初期サーバー コンポーネントをレンダリング → クライアント → サーバー アクション → クライアント → サーバー アクション...

つまり、リクエスト レベルでのみ存在する状態から、ページ間のナビゲーションと互換性のある

永続的に存在する に変わります。

Server Actions have been fixed


両方の世界 (サーバー/クライアント) 間でデータを共有します

例:


app.post("/api/search", async (req, res) => {
  const { query } = req.body;
  const data = await search(query);
  res.json(data);
});
この例では、クライアントで使用されるのではなく、サーバー アクションで再利用され、最終的にサーバー アクションの再レンダリングで再利用されるように、エラー ストア プロパティの存続期間を延長します。この場合、非機密データであるため、暗号化する必要はありません。このコード例は、再レンダリングも含めてすべてサーバー上で行われ、サーバー上でのこのレンダリング後にエラーが表示されます。サーバー RPC は HTML チャンクをストリーミングで送信し、クライアント RPC はそれを処理して差分を取得し、エラーをユーザーにフィードバックします。

6. 機密データのみを暗号化する

サーバー アクション内でレンダリング レベルに存在する変数が使用される場合、

Next.js 14 などの多くのフレームワークは、セキュリティ レベルでこのデータを暗号化して、次で使用されるデータのスナップショットを作成します。レンダリングの時間。これは多かれ少なかれ問題ありませんが、データの暗号化には常に関連する計算コストがあり、常に機密データであるとは限りません。

Brisa では、これを解決するためにさまざまなリクエストがあり、最初のレンダリングでは値があり、サーバー アクションではこのリクエストに含まれる値をキャプチャできます。


<input
  debounceInput={300}
  onInput={async (e) => {
    // All this code only runs on the server
    const data = await search(e.target.value);
    store.set("query", data);
    store.transferToClient(["query"]);
  }}
/>
これは場合によっては便利ですが、常に役立つわけではありません。たとえば、Math.random を実行すると、最初のレンダリングとサーバー アクションの実行では確実に異なります。


<input
  onInput={(e) => {
    // debounce
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      fetch("/api/search", {
        method: "POST",
        body: JSON.stringify({ query: e.target.value }),
      })
        .then((res) => res.json())
        .then((data) => {
          setState({ data });
        });
    }, 300);
  }}
/>

これが、サーバー ストアからクライアント ストアデータを転送するために、「アクション シグナル」の概念を作成した理由です。 >、開発者は、暗号化するかしないを自由に決定できます。

場合によっては、サーバー アクションからデータベースにクエリを実行する代わりに、関連付けられた暗号化が必要な場合でも、最初のレンダリングですでに存在するデータを転送したい場合があります。これを行うには、単に次を使用します:


app.post("/api/search", async (req, res) => {
  const { query } = req.body;
  const data = await search(query);
  res.json(data);
});
次の場合:


<input
  debounceInput={300}
  onInput={async (e) => {
    // All this code only runs on the server
    const data = await search(e.target.value);
    store.set("query", data);
    store.transferToClient(["query"]);
  }}
/>
Web コンポーネント (クライアント) 内では常に暗号化されますが、サーバー上では常に復号化されます。

: Brisa は暗号化に aes-256-cbc を使用します。これは、OpenSSL が推奨する情報を安全に暗号化するために使用される暗号化アルゴリズムの組み合わせです。暗号化キーはプロジェクトのビルド中に生成されます。

Server Actions have been fixed


両方の世界 (サーバー/クライアント) 間で暗号化されたデータを共有します

結論

Brisa では、Web コンポーネントの簡単な作成をサポートしたいと考えていますが、目標は、クライアント コードなしで SPA を作成し、純粋にクライアントとの対話である場合、または Web API に触れる必要がある場合にのみ Web コンポーネントを使用できるようにすることです。このため、サーバー アクションは、クライアント コードを記述せずにサーバーとの対話を可能にするため、非常に重要です。

Brisa を試してみることをお勧めします。ターミナルで次のコマンドを実行するだけです: bun create brisa か、いくつかの例を試してどのように機能するかを確認してください。

参考文献

    サーバーアクションの伝達
  • サーバーアクションの動作
  • サーバーアクションを含むフォーム
  • ネストされたアクション
  • サーバー側の検証とエラー処理
  • サーバーアクションのデバウンス
  • 楽観的な最新情報
  • 実際の再レンダリング
  • サーバーアクションで別のページに移動します
  • Cookie へのアクセス
  • サーバーアクションのセキュリティ
  • アクションシグナル
  • 機密データを転送する
  • サーバーアクションのプロパティ
  • リバース プロキシでのサーバー アクションの使用

以上がサーバーアクションが修正されましたの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。