Background:

A task is very time-consuming and consumes a lot of computing power in the background, so when exiting the page, ask the front end to send a request to kill the task.

At first, I thought the requirement was simple: just send the request and kill the task before going to another route.

However, reality hit me in the face, because the exit page scenario is more than switching routes ~

Exit page scenario:

  1. Still on this site, jump to other routes
  2. Refreshing/closing the page also requires sending a request to kill the task

Still on this site, jump to other routes

This is relatively simple and can be done in Vue by routing away the hook beforeRouteLeave:

 beforeRouteLeave(to, from, next) {
    if(Task running) {// Send the request
    }else{
        next(true) // The user leaves}}Copy the code

Page refresh/page close:

However, beforeRouteLeave does not execute when the page is refreshed, and the following two apis come to mind.

beforeunloadandunload

Beforeunload Triggers when a browser window closes or refreshes:

Introduction:

Using this API prevents the page from closing directly, and the user decides whether to not close/refresh the current page by clicking the OK/cancel button.

You’ve all seen this in Chrome:

How to use

This API is very simple to use. Just listen for this event while the page is loading and return a value that can be converted to true when a popover is required.

// before the page unloads
let killTask = false; // Whether to kill the task
window.onbeforeunload = e= > {
  if(Task running && page) {killTask =true;
    return 'You may have unsaved data'; // Popover titles can be modified in some browsers
  } else {
    killTask = false;
  }
  // No popover occurs without a return value that can be converted to true
};
Copy the code

The browser behavior when this popup appears:

The following behaviors are based on Chorme:

  1. Focus: Focus will remain on this popover until you click Cancel/OK

  2. You can’t do anything on a page that pops up

  3. In other pages, you can only perform simple click operations. Pop-ups still exist in the middle of the page, so you can’t use the keyboard.

  4. Keyboard: The keyboard is bound to the pop-up window and can only be cancelled/confirmed by pressing Esc and Enter

  5. Popovers are not the DOM of the page, they are the behavior of the browser

  6. User cancel/confirm, no callback API, no way to know

Popover title:

Refresh page title in Chrome: Reload this site?

Title of closed page in Chrome: Leave this site?

Right now, most browsers don’t allow you to change the title of the popover, for safety reasons, to make sure you don’t get misinformation.

Confused:

At first, I thought that since I could intercept the user’s refresh/close operation, the above popover appeared and the requirement was done.

Then it turns out that the browser doesn’t provide a callback for the user to hit OK/cancel the refresh page.

Here I fall into confusion, staring at the beforeunload API thinking about the meaning of life (actually in a daze), staring at, staring at beforeunload BEFORE I also think of the UNLOAD API.

Instantly fired up, why not try this Unload?

unloadThis event is triggered when the page is being uninstalled

introduce

This event is triggered when a page is being uninstalled. The event cannot be cancelled and is irreversible.

use

Simply listen for the event.

window.onunload = e= > {}
Copy the code

Combined needs:

KillTask is a variable defined at beforeUnload. Each time a callback is entered, a value is assigned to killTask to determine when a request can be sent to kill the task.

window.onunload = e= > {
  if(killTask && corresponding page) {// Send the request}};Copy the code

Here we must think we have made the requirement, in fact, there is no!

Unable to send asynchronous request

I used AXIos to send the request, the request was sent, but it was cancelled, the server never received the request, as follows.

After a bit of analysis, it was discovered that axios requests were asynchronous. Google later discovered that Axios did not support synchronous requests

Finally, use the native XMLHttpRequest object to synchronize the request

And you’re done! Actually, the above is my first post, and the following is a better solution!

Defects and better Suggestions:

When I posted this article on wechat and it was picked up by Weird Dance Weekly, some of the bigwigs there gave me some advice.

Did some research, and it turns out… All right! I admit I’m a vegetable chicken.

Hey, that’s one of the benefits of blogging, sharing experience and gaining knowledge!

Performance defects:

XHR synchronization requests prevent page unloads and slow page redisplay if the page is refreshed or redirected, resulting in performance issues.

After all, sending a request to the network and getting a response can be extremely slow, either because the user’s network environment is poor, or because the server is down and the request never comes back…

Based on performance issues, they recommended using Beacon instead of XHR, and after a search…

Beacon API

  1. The Beacon API is used to send small amounts of data to the server via POST requests.
  2. BeaconA non-blocking request requires no response

Perfect solution to performance defects:

  1. The browser willBeaconThe request is queued to be executed when it is idle and immediately returned to control
  2. It’s inunloadIt can also be sent asynchronously without blocking page refreshing or redirecting operations.

So **Beacon is a perfect solution to the above mentioned performance defects caused by blocking XHR synchronization requests **.

Use:navigator.sendBeacon()

Complete API:

let result = navigator.sendBeacon(url, data);
Copy the code

Beacon hangs under navigator, which is its full API.

Result is a Boolean value representing the result of the request being sent:

  • If the browser accepts and queues the request, trU is returned
  • Return false if there is a problem in this process

Navigator. sendBeacon accepts two arguments:

  1. Url: The requested URL. The request is a POST request.
  2. Data: indicates the data to be sent. The data types can be: ArrayBufferView, Blob, FormData, Sting.

To get the idea, use FormData to pass data:

// Create a new FormData and add a key-value pair
let data = new FormData();
data.append('hello'.'world');
let result = navigator.sendBeacon('./src', data);
if (result) { 
  console.log('Request successfully queued for execution');
} else {
  console.log('failure');
}
Copy the code

Browser support:

Supported browsers: Edge: 14+, Firefox: 31+, Chrome: 39+, Opera: 26+, IE: not supported.

Although sendBeacon is currently well supported by browsers, it’s worth doing some compatibility checks:

if (navigator.sendBeacon) {
  / / Beacon code
} else {
 // Fall back to XHR to synchronize the request or do no processing
}
Copy the code

Use Beacon in Web wroker

Because Beacon is hung under navigator, and The Web worker also has navigator, I went to look for it and found it for me.

Here is a chestnut provided by MDN, you can click on it to have a look.

PS: If you are not familiar with Web workers, please read this article

Beacon Other Related

  • Client optimization: Beacon requests can be combined with other requests for simultaneous processing, especially in mobile environments.
  • Beacon is mostly used as a front-end burial point to monitor user activities, and its original intention is based on this.

summary

This article covers three apis, beforeUnload, unload and Beacon. This API is expected to be known by fewer people. Remember to use these apis when you need to send requests before a page is unloaded.

The above 2019.02.19

Blog, front-end accumulation of documents, public account, GitHub, Wx :OBkoro1, email: [email protected]

References:

MDN

Discussion on the problem of statistical data loss when the page jumps

Log activity using the Web Beacon API