This is an HTML page.

  • Page A opens page B. What is the communication mode between page A and page B?
  • If page B closes properly, how do I notify page A?
  • Page B crashes unexpectedly, and how to notify page A?

Page A Displays page B. The communication between page A and page B is displayed

As far as I know, the communication methods of page A and B are as follows:

  • Url and the cords
  • postmessage
  • localStorage
  • WebSocket
  • SharedWorker
  • Service Worker

Url and the cords

Url pass parameters nothing to say

<! -- A.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, "> < span style =" box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 13px! Important; word-break: inherit! Important;" <script> window.name = 'A' function openB() { window.open("B.html", "B") } window.addEventListener('hashchange', Function () {// listen to hash alert(window.location.hash)}, false); </script> </body> </html>Copy the code

B:

<! -- B.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, "> <title>B</title> <button type="button" onclick="sendA()"> sendA page message </button> </head> <body> <h1>B <span>< script> window.name = 'B' window. onbeforeUnload = function (e) {window.open(' a.html #close', "A") return 'Sure to leave this page? '; } </script> </body> </html>Copy the code

Page A communicates with page B via A URL passing parameter, and again by listening for A HashChange event to communicate with A when page B is closed

postmessage

PostMessage is an API introduced by H5. The postMessage() method allows scripts from different sources to communicate effectively in an asynchronous manner. It can be used for cross-text documents, multi-window, cross-domain messaging, and data communication between Windows, making it an effective solution for cross-domain communication. Hardly too easy to use

Page A opens page B, and page B sends A message to page A:

<! -- A.html -->
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>A</title>
</head>
<body>
    <h1>A page</h1>
    <button type="button" onclick="openB()">B</button>
    <script>
        window.name = 'A'
        function openB() {
            window.open("B.html? code=123"."B")}window.addEventListener("message", receiveMessage, false);
        function receiveMessage(event) {
            console.log('Received message:', event.data)
        }
    </script>
</body>
</html>
Copy the code
<! -- B.html -->
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>B</title>
    <button type="button" onclick="sendA()">Send A page message</button>
</head>
<body>
    <h1>B page</h1>
    <span></span>
    <script>
        window.name = 'B'
        function sendA() {
            let targetWindow = window.opener
            targetWindow.postMessage('Hello A'."http://localhost:3000");
        }
    </script>
</body>
</html>
Copy the code

localStorage

// A
localStorage.setItem('testB'.'sisterAn');

// B
let testB = localStorage.getItem('testB');
console.log(testB)
// sisterAn
Copy the code

Note: localStorage only allows you to access a Document source (origin) object Storage; The stored data will be stored in the browser session. If page B opened by user A and page A are from different sources, user A cannot access the same Storage

WebSocket

Based on the server page communication mode, the server can take the initiative to push information to the client, the client can also take the initiative to send information to the server, is a real two-way equal dialogue, belongs to the server push technology

SharedWorker

The SharedWorker interface represents a particular type of worker that can be accessed from several browsing contexts, such as several Windows, iframe, or other workers. They implement a different interface from the normal worker, with a different global scope, SharedWorkerGlobalScope.

// A.html
var sharedworker = new SharedWorker('worker.js')
sharedworker.port.start()
sharedworker.port.onmessage = evt= > {
	// evt.data
    console.log(evt.data) // hello A
}

// B.html
var sharedworker = new SharedWorker('worker.js')
sharedworker.port.start()
sharedworker.port.postMessage('hello A')

// worker.js
const ports = []
onconnect = e= > {
const port = e.ports[0]
   ports.push(port)
   port.onmessage = evt= > {
       ports.filter(v= >v! == port)// In order to be close to the implementation of other solutions, I delete myself
       .forEach(p= > p.postMessage(evt.data))
   }
}
Copy the code

Service Worker

Service Worker is a Worker that can run in the background for a long time and can realize two-way communication with the page. Service workers among multiple page sharing can be shared, and Service workers can be used as the message processing center (central station) to achieve broadcast effect.

/ / register
navigator.serviceWorker.register('./sw.js').then(function () {
    console.log('Service Worker registered successfully ');
})

// A
navigator.serviceWorker.addEventListener('message'.function (e) {
    console.log(e.data)
});

// B
navigator.serviceWorker.controller.postMessage('Hello A');
Copy the code

If page B closes properly, how do I notify page A

When the page closes normally, window.onbeforeUnload is performed first, and window.onunload is performed later. We can communicate to page A in these two methods

Page B crashes unexpectedly, and how to notify page A

The page closes normally, we have related API, crash is different, the page is not visible, JS is not running, then what other way can get B page crash?

After searching the whole web, we found that we can use the Load and beforeUnload events of window objects to obtain the crash of B page through heartbeat monitoring

 window.addEventListener('load'.function () {
      sessionStorage.setItem('good_exit'.'pending');
      setInterval(function () {
         sessionStorage.setItem('time_before_crash'.new Date().toString());
      }, 1000);
   });

   window.addEventListener('beforeunload'.function () {
      sessionStorage.setItem('good_exit'.'true');
   });

   if(sessionStorage.getItem('good_exit') &&
      sessionStorage.getItem('good_exit')! = ='true') {
      /* insert crash logging code here */
      alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));
   }
Copy the code

Using the Load and beforeUnload events to implement crash monitoring is as follows:

Pictures from: zhuanlan.zhihu.com/p/40273861

This solution cleverly takes advantage of the failure to trigger the beforeUnload event when a page crashes.

When the page is loaded (load event), the state of good_exit is recorded as pending in sessionStorage. If the user exits properly (beforeUnload event), the state is changed to True. If the user crashes, the state is still pending. When the user visits the page for the second time (the second load event), check the state of good_exit. If it is still pending, it can be inferred that the page crashed last time!

There is a problem with using a sessionStorage to save the state. The sessionStorage value will be lost when the user closes the B page, so use a Service Worker instead:

  • The Service Worker has its own Worker thread, separate from the web page. If the web page crashes, the Service Worker generally does not crash;
  • Service workers typically have a life span longer than a web page and can be used to monitor the status of a web page;
  • Web pages can be through the navigator. ServiceWorker. Controller. PostMessage API to run their own SW send messages

Based on the above advantages, a complete design process is as follows:

  • After the page is loaded, B sends a heartbeat to SW every 5s through postMessage API to indicate that he is online. Sw registers the online page and updates the registration time.
  • B when the page is doing beforeUnload, the postMessage API informs itself that the page has been closed normally, sw clears the registered page;
  • If B crashes while running, the running status in SW will not be cleared, and the update time will stay at the last heartbeat before the crash.
  • If the Service Worker of page A checks the registration page every 10 seconds and finds that the registration time has exceeded A certain time (for example, 15s), it determines that the page has crashed.

The code is as follows:

// B
if(navigator.serviceWorker.controller ! = =null) {
  let HEARTBEAT_INTERVAL = 5 * 1000 // The heart beats every five seconds
  let sessionId = uuid() // the unique id of the page session
  let heartbeat = function () {
    navigator.serviceWorker.controller.postMessage({
      type: 'heartbeat'.id: sessionId,
      data: {} // Additional information, the additional data reported if the page crashes})}window.addEventListener("beforeunload".function() {
    navigator.serviceWorker.controller.postMessage({
      type: 'unload'.id: sessionId
    })
  })
  setInterval(heartbeat, HEARTBEAT_INTERVAL);
  heartbeat();
}
Copy the code
// Check the system every 10 seconds. If no heartbeat beats within 15 seconds, the system considers that the system has crashed
const CHECK_CRASH_INTERVAL = 10 * 1000 
const CRASH_THRESHOLD = 15 * 1000
const pages = {}
let timer
function checkCrash() {
  const now = Date.now()
  for (var id in pages) {
    let page = pages[id]
    if ((now - page.t) > CRASH_THRESHOLD) {
      / / report to crash
      delete pages[id]
    }
  }
  if (Object.keys(pages).length == 0) {
    clearInterval(timer)
    timer = null
  }
}

worker.addEventListener('message'.(e) = > {
  const data = e.data;
  if (data.type === 'heartbeat') {
    pages[data.id] = {
      t: Date.now()
    }
    if(! timer) { timer =setInterval(function () {
        checkCrash()
      }, CHECK_CRASH_INTERVAL)
    }
  } else if (data.type === 'unload') {
    delete pages[data.id]
  }
})
Copy the code

Reference:

  • How do I monitor crashes?
  • Are you OK?

Three minutes a day, advance one