【JS応用|実務向け】ブラウザタブ間連携の決定版!SharedWorkerで実現する効率的な状態共有

1. 導入

Webアプリケーション開発において、「複数のブラウザタブやウィンドウで同じ状態を同期したい」という要件は頻繁に発生します。LocalStorageのstorageイベントやBroadcast Channel APIで解決できるケースもありますが、これらは「データの永続化」や「単純なメッセージ送信」には向いていても、複雑なロジックの共有や計算結果のキャッシュには不向きです。SharedWorkerを活用すれば、バックグラウンドで独立したスクリプトを動かし、複数のタブ間でメモリ状態を共有・同期することが可能になります。これにより、リソースの無駄な消費を抑え、アプリのパフォーマンスを劇的に向上させることができます。

2. 基礎知識

SharedWorkerは、Web Workerの一種です。通常のWorkerが「1つのタブにつき1つのWorker」と紐付くのに対し、SharedWorkerは「同じオリジン(ドメイン・プロトコル・ポート)であれば、複数のタブから同じWorkerインスタンスに接続」できます。
仕組みとしては、Workerがメインスレッドとは別のプロセスで動作し、各タブからの接続(ポート)を管理します。メッセージングを通じてデータのやり取りを行うため、メインスレッドのUIレンダリングをブロックすることなく、重い計算やWebSocket接続の管理などを一箇所に集約できるのが最大の強みです。

3. 実装/解決策

SharedWorkerを実装するには、専用のJSファイルを用意し、メインスレッド側でインスタンス化してポート接続を確立します。
手順は以下の通りです。
1. SharedWorker用のスクリプトを作成する。
2. onconnectイベントで各タブからの接続を待ち受け、ポートを保持する。
3. ポートを通じてメッセージを相互送受信する。

4. サンプルプログラム

まずはSharedWorker用のファイル(shared-worker.js)を作成します。

// shared-worker.js
// 接続されたポートのリストを保持
const ports = [];

self.onconnect = (e) => {
const port = e.ports[0];
ports.push(port);

port.onmessage = (event) => {
// 全ての接続済みタブに対してメッセージをブロードキャストする
ports.forEach((p) => {
p.postMessage({
sender: ‘SharedWorker’,
data: `受信したデータ: ${event.data.message}`,
timestamp: new Date().toLocaleTimeString()
});
});
};

port.start();
};

次に、メインスレッド(index.js)で呼び出します。

// index.js
const worker = new SharedWorker(‘shared-worker.js’);

// 接続の確立
worker.port.start();

// メッセージの送信
worker.port.postMessage({ message: ‘こんにちは、SharedWorker!’ });

// メッセージの受信
worker.port.onmessage = (e) => {
console.log(‘Workerからの応答:’, e.data);
};

5. 応用・注意点

SharedWorkerを実務で扱う際の注意点がいくつかあります。
まず、デバッグの難しさです。Chromeであれば「chrome://inspect/#workers」からデバッグ画面を開く必要がありますが、複数タブが絡むため挙動の追跡が複雑になりがちです。
次に、ブラウザサポートです。Safariでのサポートは限定的であり、現時点ではFirefoxでもデフォルトで無効化されている場合があります。重要なロジックをSharedWorkerだけに依存させるのではなく、フォールバック(Broadcast Channel APIなど)を併用するか、ブラウザの対応状況を確認することが不可欠です。
また、SharedWorkerはリロードしても存続する場合があるため、状態のリセット処理を適切に実装しないと、古いデータが残り続けるバグが発生しやすくなります。接続が切れた際のクリーンアップ処理をしっかり書くことが、堅牢なフロントエンド構築の鍵となります。

タイトルとURLをコピーしました