Today, I visited Github without logging in to some pages, and then logged in to some pages. When you switch to another page, you will see the following prompt:

So how does this work? We can think of one way is localStorage, in a page login, modify localStorage state, other pages in the display, read the latest state, and then display the prompt:

// Login page
localStorage.setItem('login'.true);// Other pages
document.addEventListener("visibilitychange".function() {
	if (localStorage.setItem('login') = = ='true') {
		alert('You are logged in, please refresh the page'); }}Copy the code

However, Github didn’t do this and couldn’t find the relevant field in localStorage. After a bit of searching, it turned out that they were implemented using sharedWorker. Let’s take a look at SharedWorker

What is a sharedWorker

As the name implies, sharedWorker is a type of worker that can be shared by all pages of the same origin. As with Worker apis, you can register an instance of sharedWorker by passing in the URL of js:

let myWorker = new SharedWorker('worker.js');
Copy the code

However, different from ordinary workers, 1. The same JS URL will only create a sharedWorker. When other pages use the same URL to create a sharedWorker, the created Worker will be reused, and the Worker is shared by those pages. 2 The sharedWorker sends and receives messages through port

Next, let’s look at how messages are sent and received between the worker and the page.

messagePort

Suppose we have two js, one is page.js running in the page, and the other is worker.js running in the worker. So we need to register a sharedWorker in page.js with the following code:

// page.js
let myWorker = new SharedWorker('worker.js');
// page sends messages via worker port
myWorker.port.postMessage('hum');
// page receives messages via worker port
myWorker.port.onmessage = (e) = > console.log(e.data);

// worker.js
onconnect= function(e) {
	const port = e.ports[0];
	port.postMessage('ha hey');
	port.onmessage = (e) = > {
		console.log(e.data); }}Copy the code

Debugging sharedWorker

In the example above, we use console.log in the worker to print messages from the page. Where can WE see the printed log? We can type ‘Chrome ://inspect’ into the browser’s address bar

, then select shared workers in the sidebar, and you can see all the workers currently running in the browser. Click Inspect to open a developer tool, and you’ll see the output log.

Here we see that our worker name is Untitled because the sharedworker constructor also supports passing in a second argument as a name:

let myWorker = new SharedWorker('worker.js'.'awesome worker');
Copy the code

Multiple pages publish messages

Going back to the example at the beginning of this article, we have implemented communication between pages and workers, so how do we get workers to send messages to multiple pages? One idea is to cache the port as a port pool, so that when we need to broadcast messages to all pages, we can iterate over the port and send messages:

// worker js
const portPool = [];
onconnect= function(e) {
	const port = e.ports[0];
	// Add port to portPool at connect
	portPool.push(port);
	port.postMessage('ha hey');
	port.onmessage = (e) = > {
		console.log(e.data); }}function boardcast(message) {
	portPool.forEach(port= >{ port.portMessage(port); })}Copy the code

So we basically have the ability to broadcast messages to multiple pages.

Clear invalid ports

One problem with the above implementation is that ports in the workerPool are not automatically cleared after the page is closed, resulting in a waste of memory. We can notify the shared worker that the page will be closed before the page is closed, and then have the worker remove the invalid messagePort from the portPool.

/ / page
window.onbeforeunload = () = > {
  myWorker.port.postMessage('TO BE CLOSED');
};

// worker.js
const portPool = [];
onconnect = function(e) {
  var port = e.ports[0];
  portPool.push(port);
  port.onmessage = function(e) {
    console.log(e);
    if (e.data === 'TO BE CLOSED') {
      const index = ports.findIndex(p= > p === port);
      portPool.splice(index, 1);
    }
    var workerResult = 'Result: ' + (e.data[0] * e.data[1]); port.postMessage(workerResult); }}function boardcast(message) {
	portPool.forEach(port= >{ port.portMessage(port); })}Copy the code

In this way, we have implemented a simple multi-page broadcast sharedWorker. We can use it to broadcast the time:

setInterval(() = > boardcast(Date.now()), 1000);
Copy the code

reference

Developer.mozilla.org/en-US/docs/… Github.com/mdn/simple-…