Chrome 拡張機能を React などで構成された SPA で動作させる方法
はじめに
とあるページ向けに作っていた拡張機能が上手く動作しなくなったので確認すると、React.js を利用した SPA に変更されていた。
リロードするとちゃんと動作するのだが、クリックなどでページ遷移すると拡張機能が動作しないので、対応方法について調べた。
続編: Chrome 拡張機能で background scripts から content scripts にメッセージを送信する
TL;DR
chrome.tabs.onUpdated.addListener
でページ遷移を検知chrome.tabs.executeScript
で JS を注入- DOM 追加するような場合は、追加位置によっては残存するので適宜削除する
目次
環境・条件
- Chrome バージョン: 78.0.3904.70(Official Build) (64 ビット)
詳細
ChromeExtensionをSPAに対応したお話 - 生涯未熟 がとても参考になった。
chrome.tabs.onUpdated.addListener
と chrome.tabs.executeScript
を使えば良いとのこと。
どちらも使ったことがないので、使い方も含めて調べてまとめた。
リポジトリ: 17number/chrome-extension-with-spa-example
chrome.tabs.onUpdated.addListener について
まずは chrome.tabs.onUpdated.addListener
の確認、名前の通りタブの更新時に発火する。実際に使ってみる。
manifest.json
。permissions
に tabs
を指定していないと、コールバック関数でタブの URL が取得できないので注意。
1 | { |
background.js
。changeInfo
で具体的なイベントの情報が、tab
で各種情報が取得できる。
具体的に何が取れるのかをログ表示してみるだけのプログラム。Google 検索ページにアクセスすると、updated: https://google.com/xxx
を表示。
1 | chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { |
changeInfo.status === "complete"
でタブ更新が完了したかどうかを判別できる。
拡張機能が動作する URL を限定したい場合は location.href
ではなく tab.url
を使う。以下ログを見れば分かるが、location.href
には拡張機能の URL が格納されている。
1 | changeInfo: |
ちなみに、↑のログはバックグラウンドページ側に表示されるので注意。(Google を表示しているタブ側ではない)
chrome.tabs.executeScript について
chrome.tabs.executeScript
の確認。
manifest.json
。permission
に対象の URL (今回は "https://www.google.com/search*"
) を追加。
1 | { |
background.js
。Google 検索(https://www.google.com/search
)ページの読み込みが完了したら、executeScript
を実行。hello.js
を注入する。
1 | chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { |
hello.js
は console.log
で hello
と表示するだけ。
1 | console.log("hello"); |
これで Google 検索をすると、検索が完了するたびに Google のタブ側のコンソールに hello
が表示される。(今度はバックグラウンドページ側ではない)
コンソールだと分かりづらいので、ボタンをページ中に挿入してみる。hello.js
を以下に変更。
1 | document.body.firstElementChild.insertAdjacentHTML("afterbegin", "<button>hello</button>"); |
これで拡張機能を再読込して動かすとこうなる。
「タブ更新を検知 → バックグラウンドから JS を注入 → 注入された JS でページを拡張」という一連の流れを確認できた。
SPA で試す
【Vue.js, React, Angular】事例から見るSPAフレームワーク - Qiita によると、Airbnb が React.js 使ってるとのことなので、Airbnb で動作するように変更する。
manifest.json
。通常の枠組み(Content Scripts)だと上手くいかないよね、も合わせて確認するために content_scripts
の記述も追加。
1 | { |
background.js
は変更なし。
hello.js
。SPA なので(追加位置によっては)既存の追加した DOM が削除されない。なので、残存している場合には削除するようにしている。乱数は SPA でのページ遷移でも拡張機能がちゃんと動作できているかの識別用。
1 | if (document.getElementById('from-background')) |
content.js
。処理内容は hello.js
と同じ。
1 | (() => { |
この拡張機能を読み込んで Airbnb でページ遷移すると、hello from content
の方は初期表示時こそボタン追加されるものの、乱数が変更されない(つまり、その後拡張機能の JS が動作していない)ことが分かる。
一方で hello from background
の方は、ページ遷移ごとに乱数値が書き換わっていて、SPA でもページ遷移を検出して動作できていることが分かる。
まとめ
chrome.tabs.onUpdated.addListener
でページ遷移を検知chrome.tabs.executeScript
で JS を注入- DOM 追加するような場合は、追加位置によっては残存するので適宜削除する
その他・メモ
参考にした記事(ChromeExtensionをSPAに対応したお話 - 生涯未熟)も結構前なので、もっと良いやり方があるかもしれない。
参考文献
- Content Scripts - Google Chrome
- chrome.tabs - Google Chrome
- ChromeExtensionをSPAに対応したお話 - 生涯未熟
- JavaScript - chrome.tabsのonUpdatedの処理タイミングについて|teratail
- 【Vue.js, React, Angular】事例から見るSPAフレームワーク - Qiita
関連記事
- Chrome 拡張機能で background scripts から content scripts にメッセージを送信する
- Chrome 拡張機能で CSV ファイルを生成してダウンロードする方法
- Vue.js を使ってメインページ側で動作する Chrome 拡張機能を開発する方法
- vue-web-extension を使って Chrome 拡張機能を開発する方法
- webpack & Babel を使って Chrome 拡張機能を開発するためのテンプレート(Hot Reload 付き)
- jQuery Select2 で、初期値の設定と選択状態のクリア
- JavaScript で URL のクエリパラメータを操作する方法
- axios で添付ファイルありのリクエスト(multipart/form-data の POST)