1. ホーム
  2. Web制作
  3. html5

html5で複数ページ通信を行うsharedWorkerのコード例

2022-01-21 10:44:20

今日、githubで遊んでいて、ログインせずにいくつかのページを閲覧し、あるページでログインした。別のページに切り替えたところ、以下のようなプロンプトが表示されました。

では、これはどのように機能するのでしょうか。一つの方法として考えられるのは、ページがログインしたときにlocalStorageの状態を変更し、他のページは表示されたときに最新の状態を読み、プロンプトを表示して

// The logged-in 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');
	}
}

しかし、githubではそのようなことはなく、localStorageに該当するフィールドが見つかりませんでした。いろいろ探した結果、sharedWorkerで実装されていることがわかりました。そこで、sharedworkerを見てみます。

sharedWorkerとは

sharedWorker は、その名のとおり、同じオリジンのすべてのページで共有できるタイプのワーカーです。Worker apiと同様に、js urlを渡すことでsharedWorkerのインスタンスを登録することができます。

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

でも、普通の労働者と違って
1 同じjs urlでsharedWorkerを作成するだけで、その後同じurlを使用してsharedWorkerを作成した他のページは、作成されたワーカーを再利用し、そのワーカーはそれらのページで共有されます。
2 sharedWorkerは、ポート経由でメッセージを送受信します。

次に、ワーカーとページの間で具体的にどのようにメッセージが送受信されるかを見ていきます。

メッセージポート

ページで動作するpage.jsと、ワーカーで動作するworker.jsの2つのjsがあるとします。

// page.js
let myWorker = new SharedWorker('worker.js');
// page sends a message through the worker port
myWorker.port.postMessage('hmmm');
// page receives message via worker port
myWorker.port.onmessage = (e) => console.log(e.data);

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

sharedWorkerのデバッグ

上記の例では、ワーカーで console.log を使ってページからのメッセージを出力しましたが、出力されたログはどこで見ることができるでしょうか?ブラウザのアドレスバーに `chrome://inspect' と入力し、サイドバーで shared workers を選択すると、ブラウザで現在実行中のすべてのワーカーを見ることができます。inspect をクリックすると開発者ツールが開き、出力ログを見ることができます。

ここで、ワーカーの名前が untitled であることがわかります。これは、sharedworker コンストラクタが、名前として 2 番目の引数を渡すこともサポートしているからです。

let myWorker = new SharedWorker('worker.js', 'awesome worker');

複数ページ投稿メッセージ

冒頭の例に戻ると、先ほどページとワーカーの通信を実装しましたが、ではワーカーから複数のページにメッセージを送るにはどうすればよいのでしょうか。1つのアイデアとして、ポートをポートプールとしてキャッシュし、すべてのページにメッセージをブロードキャストする必要があるときに、ポートを反復してメッセージを送信することができます: ポートプールには、次のようなものがあります。

// worker js
const portPool = [];
onconnect= function(e) {
	const port = e.ports[0];
	// Add the port to the portPool at connect time
	portPool.push(port);
	port.postMessage('haha');
	port.onmessage = (e) => {
		console.log(e.data);
	}
}

function boardcast(message) {
	portPool.forEach(port => {
		port.portMessage(port);
	})
}

これは基本的に、複数のページにメッセージをブロードキャストする機能を提供するものです。

無効なポートを消去する

上記の実装の問題点として、ページを閉じた後にWorkerPool内のポートが自動的にクリアされないため、メモリの無駄遣いになってしまうことが挙げられます。ページが閉じる前に共有ワーカーにページが閉じることを通知し、ワーカーに無効な messagePort を portPool から削除させればよいのです。

// The 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);
	})
}

このようにして、簡単な複数ページのブロードキャスト用sharedWorkerを実装し、ちょっとした時間のブロードキャストに利用することができます。

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

参考文献

https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker
https://github.com/mdn/simple-shared-worker

この記事は、html5のsharedWorkerについて、マルチページ通信のサンプルコードを実現するために導入され、より関連するhtml5 sharedWorkerマルチページ通信の内容は、スクリプトホーム以前の記事を検索するか、次の関連記事を閲覧を続けてください、私はあなたが将来的に多くのスクリプトホームをサポートすることを願っています