> 웹 프론트엔드 > CSS 튜토리얼 > 사용자가 페이지를 떠날 때 HTTP 요청을 안정적으로 보냅니다.

사용자가 페이지를 떠날 때 HTTP 요청을 안정적으로 보냅니다.

William Shakespeare
풀어 주다: 2025-03-14 10:06:09
원래의
468명이 탐색했습니다.

사용자가 페이지를 떠날 때 HTTP 요청을 안정적으로 보냅니다.

대부분의 경우 다른 페이지를 탐색하거나 양식을 제출하는 등 사용자가하는 일을 기록하기 위해 일부 데이터가 포함 된 HTTP 요청을 보내야합니다. 링크를 클릭 할 때 일부 정보를 외부 서비스로 전송하는이 예를 고려하십시오.

 <a href="https://www.php.cn/link/3cbfb2330b21840b385a45c958602663">페이지로 이동하십시오</a>

document.getElementById ( 'link'). addeventListener ( 'click', (e) => {
  페치 ( "/log", {
    방법 : "post",
    헤더 : {
      "Content-Type": "Application/JSON"
    }, 
    바디 : json.stringify ({
      일부 : "데이터"
    })
  });
});
로그인 후 복사

여기에는 특히 복잡한 것이 없습니다. 링크는 평소와 같이 작동하지만 ( e.preventDefault() )를 사용하지 않았지만 해당 동작이 발생하기 전에 게시물 요청이 트리거됩니다. 응답을 기다릴 필요가 없습니다. 내가 방문하는 서비스로 보내고 싶습니다 .

언뜻보기에는 요청 예약이 동기화되었다고 생각할 수 있으며, 그 후 페이지에서 계속 탐색하고 다른 서버가 요청을 성공적으로 처리합니다. 그러나 이것이 항상 그런 것은 아닙니다.

브라우저는 공개 HTTP 요청이 유지 될 것이라고 보장하지 않습니다.

브라우저에서 페이지를 종료하기 위해 특정 이벤트가 발생하면 처리중인 HTTP 요청이 성공할 것이라는 보장은 없습니다 (페이지 "종료"및 기타 상태의 수명주기에 대한 자세한 내용). 이러한 요청의 신뢰성은 네트워크 연결, 애플리케이션 성능 및 외부 서비스 자체의 구성과 같은 여러 가지 요소에 따라 다를 수 있습니다.

따라서이 순간에 데이터를 보내는 것은 신뢰할 수 없으며,이 로그에 의존하여 데이터에 민감한 비즈니스 결정을 내릴 경우 잠재적으로 중요한 문제가 발생할 수 있습니다.

이 신뢰성을 설명하기 위해 위의 코드를 사용하여 페이지가 포함 된 작은 Express 응용 프로그램을 설정했습니다. 링크를 클릭하면 브라우저가 /other 로 이동하지만 이런 일이 발생하기 전에 사후 요청이 발행됩니다.

모든 일이 일어나는 동안 브라우저의 "네트워크"탭을 열고 "Slow 3G"연결 속도를 사용하고 있습니다. 페이지로드 후 로그를 지우고 모든 것이 조용해 보입니다.

그러나 링크를 클릭하면 상황이 잘못됩니다. 탐색이 발생하면 요청이 취소됩니다.

이로 인해 외부 서비스가 실제로 요청을 처리 할 수 ​​있는지 확인할 수 없습니다. 이 동작을 확인하려면 window.location 사용하여 프로그래밍 방식으로 탐색 할 때도 발생합니다.

 document.getElementById ( 'link'). addeventListener ( 'click', (e) => {
  e.preventDefault ();

  // 요청이 대기되었지만 탐색이 발생한 직후에 취소되었습니다.
  페치 ( "/log", {
    방법 : "post",
    헤더 : {
      "Content-Type": "Application/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", {
    방법 : "post",
    헤더 : {
      "Content-Type": "Application/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) => {
  페치 ( "/log", {
    방법 : "post",
    헤더 : {
      "Content-Type": "Application/JSON"
    },
    바디 : json.stringify ({
      일부 : "데이터"
    }),
    keepalive : true
  });
});
로그인 후 복사

링크를 클릭하고 페이지 탐색을 클릭하면 요청 취소가 발생하지 않습니다.

대신, 활성 페이지가 응답을 받기를 기다리지 않기 때문에 (알 수없는) 상태를 얻습니다.

이러한 단일 라인 코드는 특히 공통 브라우저 API의 일부인 경우 쉽게 해결할 수 있습니다. 그러나 더 간단한 인터페이스로보다 중앙 집중식 옵션을 찾고 있다면 거의 동일한 브라우저 지원으로이를 수행하는 또 다른 방법이 있습니다.

navigator.sendbeacon () 사용

Navigator.sendBeacon() 함수는 일방 통화 요청 (비콘)을 보내는 데 특별히 사용됩니다. 기본 구현은 다음과 같습니다.이 구역화 된 JSON 및 "Text/Plain" Content-Type 으로 게시물을 보냅니다.

 navigator.sendbeacon ( '/log', json.stringify ({
  일부 : "데이터"
});
로그인 후 복사

그러나이 API는 사용자 정의 헤더를 보낼 수 없습니다. 따라서 데이터를 "Application/JSON"으로 보내려면 작은 조정을하고 Blob을 사용해야합니다.

 <a href="https://www.php.cn/link/3cbfb2330b21840b385a45c958602663">페이지로 이동하십시오</a>

document.getElementById ( 'link'). addeventListener ( 'click', (e) => {
  const blob = new Blob ([json.stringify ({일부 : "data"})], {type : 'Application/Json; charset = utf-8'});
  Navigator.sendbeacon ( '/log', blob);
});
로그인 후 복사

궁극적으로 우리는 동일한 결과를 얻었습니다 - 페이지가 탐색 한 후에도 요청이 완료되었습니다. 그러나 일어나고있는 일이 몇 가지 있습니다. fetch() 보다 나을 수 있습니다. 비콘은 우선 순위가 낮게 전송됩니다.

데모를 위해서는 keepalive 와 함께 fetch()sendBeacon() 모두 사용할 때 네트워크 탭에 표시되는 내용이 있습니다.

기본적으로 fetch() 는 "높은"우선 순위를 얻는 반면 Beacons (위의 "Ping"유형으로 표시됨)는 "가장 낮은"우선 순위를 갖습니다. 이것은 페이지 기능에 중요하지 않은 요청에 좋은 것입니다. 비콘 사양에서 직접 :

이 사양은 […]가 다른 시간에 중요한 운영과의 리소스 경쟁을 최소화하면서 그러한 요청이 여전히 처리되어 대상으로 전달되도록하는 인터페이스를 정의합니다.

다시 말해, sendBeacon() 은 요청이 응용 프로그램 및 사용자 경험에 실제로 중요한 요청을 방해하지 않도록합니다.

핑 속성에 대한 명예 언급

점점 더 많은 브라우저가 ping 속성을 지원한다는 것을 언급 할 가치가 있습니다. 링크에 첨부되면 작은 게시물 요청이 발행됩니다.

<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 간단한 요청 만 보내고 사용자 정의 자바 스크립트를 작성하고 싶지 않은 경우 훌륭한 도구입니다. 그러나 더 많은 콘텐츠를 보내야하는 경우 최선의 선택이 아닐 수도 있습니다.

그래서 어떤 것을 선택해야합니까?

fetch() 또는 sendBeacon() keepalive 와 함께 사용하여 마지막 초 요청을 보낼 때 확실히 상충이 있습니다. 다른 상황에 가장 적합한 접근법을 알리기 위해 다음은 다음과 같이 고려해야 할 사항이 있습니다.

다음 경우 fetch () recealive를 선택할 수 있습니다.

  • 요청을 사용하여 사용자 정의 헤더를 쉽게 전달해야합니다.
  • 게시물 요청이 아니라 서비스에 GET 요청을 발행하려고합니다.
  • 구형 브라우저 (예 : IE)를 지원하고 있으며 PolyFill을 가져 왔습니다.

그러나 다음 경우 sendBeacon ()이 더 나은 옵션 일 수 있습니다.

  • 많은 사용자 정의가 필요하지 않은 간단한 서비스 요청을하고 있습니다.
  • 당신은 더 간결하고 우아한 API를 선호합니다.
  • 귀하의 요청이 응용 프로그램에서 전송 된 다른 높은 우선 순위 요청과 경쟁하지 않도록하려고합니다.

같은 실수를 반복하지 마십시오

페이지가 종료 될 때 브라우저가 프로세스 중 요청을 처리하는 방식을 깊이 파고 들기로 선택한 이유가 있습니다. 얼마 전, 우리가 양식을 제출했을 때 즉시 요청을 시작한 후, 우리 팀은 특정 유형의 분석 로그의 빈도가 갑자기 변경된다는 것을 발견했습니다. 이 변화는 갑작스럽고 중요합니다. 역사상 우리가 보았던 것보다 약 30% 감소했습니다.

이 문제의 원인을 파고 들고 문제를 다시 나타나지 않기 위해 사용 가능한 도구는 그 날을 절약했습니다. 그러므로, 나는 이러한 도전의 뉘앙스를 이해하기를 희망하면 누군가가 우리가 겪는 고통을 피하는 데 도움이 될 수 있습니다. 행복한 기록!

위 내용은 사용자가 페이지를 떠날 때 HTTP 요청을 안정적으로 보냅니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿