この記事で扱うこと
ブラウザ拡張機能で、ある画面がデータを保存してから別画面に最新の一覧が表示されるまでの流れを追った。特定製品の実装や数値ではなく、Content Script・Background Service Worker・Side Panel のように分離された実行空間をつなぐとき、どこでも適用できるメッセージ契約と状態更新の原則だけを整理する。
先に知っておく概念
RPC(Remote Procedure Call) は、別の実行空間へ構造化されたリクエストを送り、作業結果を受け取る方式である。キャッシュ無効化(cache invalidation) は、保存済みの取得結果が古くなったと印を付け、最新値を読み直させる処理である。単一の真実の供給源(single source of truth) は、同じデータの最終基準を一つの保存先だけに置く原則である。
メモリで直接つながっていない画面たち
Web ページでユーザーが選択した内容を保存し、拡張機能の Side Panel で一覧を見せる一般的な機能を考えた。Content Script は現在タブの DOM を読める。Background Service Worker は保存と権限を担当する。Side Panel は一覧をレンダリングする。三つの空間は同時に存在していても、同じメモリオブジェクトを共有しない。
そのため、保存ボタンから一覧更新までを一行ずつ追跡した。UI の sendRequest() は runtime.sendMessage() で { type, payload } リクエストを送り、Background listener は受け取った値を runtime schema で検査する。検証を通過したリクエストだけを operation router が登録済み handler に渡し、handler は transaction を終えた後に { ok, data | error } 形式で結果を返す。
保存応答だけでは別画面は更新されない
最初は、保存応答が返ればすべての画面が新しいデータを知るように考えがちである。しかし応答はリクエストした画面にだけ返る。別の Side Panel が最新一覧を表示するには、保存後の状態変更を知らせる別経路が必要である。
ここでは、新しい全データをイベントにコピーせず、「この範囲のデータが変わった」という小さな信号だけをブロードキャストする構造を見た。Side Panel は信号を受け取ると、関連する TanStack Query cache を無効化し、IndexedDB の最新状態を読み直す。命令の成功可否を知らせる応答と、別画面に再取得のきっかけを与える通知は責務が異なるため、分けたほうが理解しやすかった。
→ invalidateQueries と staleTime
イベントを逃してもデータが消えないようにする
Side Panel が閉じていると、ブロードキャストを受け取る listener がない場合がある。それでも、変更信号がデータそのものではなく再取得のきっかけにすぎないなら問題にならない。後から Side Panel を開いたとき、最初の query が IndexedDB を読んで最新状態を得る。
この構造では IndexedDB が単一の真実の供給源であり、query cache は再生成できるコピーである。イベントを受け取れたかどうかをデータ保存の条件にしていないため、実行空間の寿命が互いに違っても一貫性を回復できる。反対に、イベント payload を新しい原本のように保存すると、イベントを逃した画面と受け取った画面の状態が分岐し得る。
学んだこと
Chrome が提供するメッセージ API と、アプリケーションの RPC 層を区別する必要がある。プラットフォームは値を運ぶが、どのリクエストを許可するか、どの handler を実行するか、失敗をどの形式で返すかは自分で設計しなければならない。
また、リクエスト応答と変更通知を同じメカニズムとして見ないほうがよい。リクエスト元は作業結果を受け取り、別画面は変更信号を受けて必要な範囲だけを再取得する。最後に、キャッシュは便宜上のコピーとして置き、最新データの基準を一つの保存先に置くと、画面が閉じて再度開かれたり Background が再起動されたりしても状態を復元しやすい。