検索
ホームページウェブフロントエンドCSSチュートリアルユーザーがページを残すと、HTTPリクエストを確実に送信します

ユーザーがページを残すと、HTTPリクエストを確実に送信します

多くの場合、別のページに移動したりフォームの送信など、ユーザーが何をしたりするかを記録するために、いくつかのデータを含むHTTPリクエストを送信する必要があります。この例を考えてみましょう。リンクをクリックするときに外部サービスに情報を送信します。

<a href="https://www.php.cn/link/3cbfb2330b21840b385a45c958602663">ページに移動します</a>

document.getElementById( 'link')。addeventlistener( 'click'、(e)=> {
  fetch( "/log"、{
    方法:「投稿」、
    ヘッダー:{
      「コンテンツタイプ」:「アプリケーション/JSON」
    }、 
    ボディ:json.stringify({
      いくつか:「データ」
    })
  });
});

ここには特に複雑なことはありません。リンクは通常どおり機能します( e.preventDefault()を使用しませんでした)が、その動作が発生する前に、POSTリクエストがトリガーされます。応答を待つ必要はありません。訪問しているサービスに送信したいだけです

一見すると、リクエストのスケジューリングが同期されていると思われるかもしれません。その後、ページからナビゲートし続け、他のサーバーがリクエストの処理を正常に処理します。しかし、これは必ずしもそうではないことがわかります。

ブラウザは、開いているHTTPリクエストが保持されることを保証しません

ブラウザ内のページを終了するために特定のイベントが発生した場合、処理されるHTTP要求が成功するという保証はありません(ページ「終了」および他の状態のライフサイクルの詳細)。これらの要求の信頼性は、ネットワーク接続、アプリケーションのパフォーマンス、さらには外部サービス自体の構成など、多くの要因に依存する場合があります。

したがって、これらの瞬間にデータを送信することは信頼性からはほど遠い場合があります。これらのログに頼ってデータに敏感なビジネス上の意思決定を行うと、潜在的に重大な問題が発生する可能性があります。

この信頼性を示すために、上記のコードを使用してページを使用して小さなExpressアプリケーションを設定します。リンクがクリックされると、ブラウザは/otherにナビゲートされますが、これが発生する前に、POSTリクエストが発行されます。

すべてが起こっている間に、ブラウザの「ネットワーク」タブを開き、「スロー3G」接続速度を使用しています。ページが読み込まれた後、私はログをクリアし、すべてが静かに見える:

しかし、リンクをクリックすると、事態はうまくいきます。ナビゲーションが発生すると、リクエストがキャンセルされます。

これにより、外部サービスが実際にリクエストを処理できることを確認することはほとんどできません。この動作を検証するために、これはwindow.locationを使用してプログラムでナビゲートするときにも起こります。

 document.getElementById( 'link')。addeventlistener( 'click'、(e)=> {
  E.PreventDefault();

  //リクエストはキューに登録されていますが、ナビゲーションが発生した直後にキャンセルされました。
  fetch( "/log"、{
    方法:「投稿」、
    ヘッダー:{
      「コンテンツタイプ」:「アプリケーション/JSON」
    }、
    ボディ:json.stringify({
      いくつか:「データ」
    })、
  });

  window.location = e.target.href;
});

これらの未完成のリクエストは、ナビゲーションがどのように、いつ発生するか、いつアクティブページが終了するかに関係なく、放棄される場合があります。

なぜキャンセルされたのですか?

問題の根本的な原因は、デフォルトでは、XHR要求( fetchまたはXMLHttpRequest経由)が非同期および非ブロッキングであることです。要求がキューになったら、リクエストの実際の作業がバックグラウンドでブラウザレベルのAPIに渡されます。

これはパフォーマンスの点で優れています - リクエストがメインスレッドを取り上げることを望んでいません。しかし、これはまた、ページが「終了」状態に入ると、放棄されるリスクがあり、背景作業を完了できるという保証がないことを意味します。この特定のライフサイクル状態のGoogleの要約は次のとおりです。

ページがアンインストールを開始し、ブラウザによってメモリからクリアされると、終了した状態になります。この状態では、新しいタスクを開始できず、進行中のタスクが長すぎると終了する場合があります。

要するに、ブラウザの設計の仮定は、ページが閉じられているときに、キューする背景プロセスの処理を継続する必要はないということです。

それで、私たちの選択は何ですか?

この問題を回避する最も明白な方法は、リクエストが応答を返すまで、ユーザーアクションを可能な限り遅らせることです。過去には、これはXMLHttpRequestでサポートされている同期フラグを使用して誤って行われました。しかし、それを使用するとメインスレッドが完全にブロックされ、多くのパフォーマンスの問題が発生します - 私は過去にこれについていくつか書いたので、このアイデアを考慮すべきではありません。実際、プラットフォームを終了しようとしています(Chrome V80はすでに削除されています)。

代わりに、このアプローチを採用する場合は、返された応答を解決する約束を待つ方が良いでしょう。戻ったら、動作を安全に実行できます。以前のコードスニペットを使用して、これは次のようになるかもしれません。

 document.getElementById( 'link')。addeventlistener( 'click'、async(e)=> {
  E.PreventDefault();

  //応答が返されるのを待ちます...
  fetch( "/log"、{
    方法:「投稿」、
    ヘッダー:{
      「コンテンツタイプ」:「アプリケーション/JSON」
    }、
    ボディ:json.stringify({
      いくつか:「データ」
    })、
  });

  //…そして、去るためにナビゲートします。
  window.location = e.target.href;
});

これは仕事をすることができますが、いくつかの自明な欠点があります。

まず、必要な動作の発生を遅らせることにより、ユーザーエクスペリエンスに影響します。分析データを収集することは、ビジネス(および将来のユーザー)にとって確かに有益ですが、それは現在のユーザーがこれらの利点を達成するためにお金を払わせるため、理想とはほど遠いものです。言うまでもなく、外部依存関係として、サービス自体のレイテンシまたはその他のパフォーマンスの問題は、ユーザーに反映されます。分析サービスからのタイムアウトにより、顧客が価値の高い操作を完了できなくなった場合、誰もが失われます。

第二に、このアプローチは、最初の音ほど信頼性がありません。一部の終了動作はプログラムで遅らせることはできないためです。たとえば、 e.preventDefault()ユーザーが[ブラウザ]タブを閉じるように遅延するのに役に立たない。したがって、せいぜい、特定のユーザーアクションからのデータの収集のみをカバーしますが、完全に信頼するには十分ではありません。

未完成のリクエストを保持するようブラウザに指示します

ありがたいことに、ほとんどのブラウザには、ユーザーエクスペリエンスを損なうことなく未完成のHTTP要求を保持するための組み込みオプションがあります。

FetchのKeepAliveロゴを使用します

fetch()を使用するときにkeepaliveフラグがtrueに設定されている場合、リクエストを開始したページが終了した場合でも、対応する要求は開いたままです。最初の例を使用して、これにより実装が次のようになります。

<a href="https://www.php.cn/link/3cbfb2330b21840b385a45c958602663">ページに移動します</a>

document.getElementById( 'link')。addeventlistener( 'click'、(e)=> {
  fetch( "/log"、{
    方法:「投稿」、
    ヘッダー:{
      「コンテンツタイプ」:「アプリケーション/JSON」
    }、
    ボディ:json.stringify({
      いくつか:「データ」
    })、
    Keepalive:本当です
  });
});

リンクとページナビゲーションをクリックすると、要求のキャンセルは発生しません。

代わりに、アクティブなページが応答が受信されるのを待つことができないという理由だけで、(未知の)状態を取得します。

このようなシングルラインコードは、特に一般的なブラウザAPIの一部である場合、簡単に修正できます。ただし、よりシンプルなインターフェイスを使用して、より集中化されたオプションを探している場合は、ほぼ同じブラウザーサポートでそれを行う別の方法があります。

Navigator.sendBeacon()関数は、一方向リクエスト(ビーコン)を送信するために特別に使用されます。基本的な実装は次のとおりです。これは、Stringified JSONと「テキスト/プレーン」 Content-Type投稿を送信します。

 navigator.sendbeacon( '/log'、json.stringify({
  いくつか:「データ」
}));

ただし、このAPIでは、カスタムヘッダーを送信することはできません。したがって、データを「アプリケーション/JSON」として送信するには、小さな調整を行い、BLOBを使用する必要があります。

<a href="https://www.php.cn/link/3cbfb2330b21840b385a45c958602663">ページに移動します</a>

document.getElementById( 'link')。addeventlistener( 'click'、(e)=> {
  const blob = new blob([json.stringify({some: "data"})]、{type: 'application/json; charset = utf-8'});
  navigator.sendbeacon( '/log'、blob);
});

最終的に、同じ結果が得られました。ページがナビゲートされた後でも、リクエストは完了しました。しかし、発生していることがいくつかあります。これは、 fetch()よりも優れている可能性があります。ビーコンは低優先度で送信されます。

デモンストレーションについては、 fetch()sendBeacon()の両方をkeepaliveで使用する場合、ネットワークタブに表示されるものを次に示します。

デフォルトでは、 fetch()は「高い」優先度を取得し、ビーコン(上記の「ping」タイプとは記載)は「最も低い」優先度を持っています。これは、ページの機能にとって重要ではないリクエストにとって良いことです。ビーコン仕様から直接:

この仕様は、[…]他の時間批判的な操作とのリソース競争を最小限に抑えながら、そのような要求がまだ処理され、目的地に配信されることを保証するインターフェイスを定義します。

言い換えれば、 sendBeacon()その要求があなたのアプリケーションとユーザーエクスペリエンスにとって本当に重要な要求を妨げないことを保証します。

ping属性に関する名誉言及

ますます多くのブラウザがping属性をサポートすることに言及する価値があります。リンクに添付されると、小さなPOSTリクエストが発行されます。

<a href="https://www.php.cn/link/fef56cae0dfbabedeadb64bf881ab64f" ping="http://localhost:3000/log">
  他のページに移動します
</a>

これらのリクエストヘッダーには、リンクがクリックされるページ(ping-from)と、リンクのhref値(ping-to)が含まれます。

 <code>headers: { 'ping-from': 'http://localhost:3000/', 'ping-to': 'https://www.php.cn/link/fef56cae0dfbabedeadb64bf881ab64f' 'content-type': 'text/ping' // ...其他标头},</code>

技術的にはビーコンの送信に似ていますが、いくつかの顕著な制限があります。

  1. リンクに使用することは厳密に制限されています。これは、ボタンクリックやフォーム送信など、他のインタラクションに関連するデータを追跡する必要がある場合に悪い選択です。
  2. ブラウザはよくサポートしますが、素晴らしいものではありません。執筆時点では、Firefoxは特にデフォルトでそれを有効にしません。
  3. リクエストでカスタムデータを送信することはできません。前述のように、せいぜいわずかなping-*ヘッダーと他のヘッダーしか入手できません。

全体として、 ping簡単なリクエストのみを送信し、カスタムJavaScriptを書きたくない場合に最適なツールです。ただし、より多くのコンテンツを送信する必要がある場合は、最良の選択肢ではない場合があります。

だから、どちらを選ぶべきですか?

keepaliveを使用してfetch()またはsendBeacon()を使用して最後の秒リクエストを送信する場合、確かにトレードオフがあります。さまざまな状況に最適なアプローチを伝えるために、考慮すべきことがいくつかあります。

次の場合には、Fetch()KeepAliveを選択できます。

  • リクエストを使用してカスタムヘッダーを簡単に渡す必要があります。
  • POSTリクエストではなく、サービスにGETリクエストを発行する必要があります。
  • 古いブラウザ(IEなど)をサポートしており、PolyFillのロードをフェッチしています。

ただし、sendbeacon()が次の場合にはより良い選択肢かもしれません。

  • 多くのカスタマイズを必要としない簡単なサービスリクエストを行っています。
  • あなたはより簡潔でエレガントなAPIを好む。
  • お客様は、リクエストがアプリケーションで送信された他の高優先度リクエストと競合しないことを確認したいと考えています。

同じ間違いを繰り返さないでください

ページが終了したときにブラウザがインプロセスのリクエストを処理する方法を深く掘り下げることを選択した理由があります。少し前に、フォームを提出したときにすぐにリクエストを発射し始めた後、私たちのチームは、特定の種類の分析ログの頻度が突然変化したことを発見しました。この変化は突然で重要です。歴史上私たちが見たものから約30%減少しています。

この問題の原因と、問題を再現しないように利用可能なツールを掘り下げると、その日は節約されました。ですから、もしあれば、これらの課題のニュアンスが、私たちが遭遇する痛みの一部を避けるのに役立つことを理解したいと思っています。ハッピーレコード!

以上がユーザーがページを残すと、HTTPリクエストを確実に送信しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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

CSSグリッドは、複雑で応答性の高いWebレイアウトを作成するための強力なツールです。設計を簡素化し、アクセシビリティを向上させ、古い方法よりも多くの制御を提供します。

CSS Flexboxとは何ですか?CSS Flexboxとは何ですか?Apr 30, 2025 pm 03:20 PM

記事では、レスポンシブデザインにおけるスペースの効率的なアラインメントと分布のためのレイアウト方法であるCSS FlexBoxについて説明します。 FlexBoxの使用量を説明し、CSSグリッドと比較し、ブラウザのサポートを詳細に説明します。

CSSを使用してWebサイトを応答するにはどうすればよいですか?CSSを使用してWebサイトを応答するにはどうすればよいですか?Apr 30, 2025 pm 03:19 PM

この記事では、ビューポートメタタグ、柔軟なグリッド、流体メディア、メディアクエリ、相対ユニットなど、CSSを使用してレスポンシブWebサイトを作成するための手法について説明します。また、CSSグリッドとフレックスボックスを使用してカバーし、CSSフレームワークを推奨しています

CSSボックスサイズのプロパティは何をしますか?CSSボックスサイズのプロパティは何をしますか?Apr 30, 2025 pm 03:18 PM

この記事では、要素の寸法の計算方法を制御するCSSボックスサイズのプロパティについて説明します。コンテンツボックス、ボーダーボックス、パディングボックスなどの値と、レイアウト設計とフォームアライメントへの影響について説明します。

CSSを使用してアニメーション化するにはどうすればよいですか?CSSを使用してアニメーション化するにはどうすればよいですか?Apr 30, 2025 pm 03:17 PM

記事では、CSS、キープロパティ、およびJavaScriptとの組み合わせを使用してアニメーションの作成について説明します。主な問題は、ブラウザの互換性です。

CSSを使用してプロジェクトに3D変換を追加できますか?CSSを使用してプロジェクトに3D変換を追加できますか?Apr 30, 2025 pm 03:16 PM

記事では、3D変換、主要なプロパティ、ブラウザの互換性、およびWebプロジェクトのパフォーマンスに関する考慮事項にCSSを使用して説明します。

CSSに勾配を追加するにはどうすればよいですか?CSSに勾配を追加するにはどうすればよいですか?Apr 30, 2025 pm 03:15 PM

この記事では、CSSグラデーション(線形、放射状、繰り返し)を使用して、ウェブサイトのビジュアルを強化し、深さ、フォーカス、および現代の美学を追加します。

CSSの擬似要素とは何ですか?CSSの擬似要素とは何ですか?Apr 30, 2025 pm 03:14 PM

記事では、CSSの擬似要素、HTMLスタイリングの強化における使用、および擬似クラスとの違いについて説明します。実用的な例を提供します。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。