傳遞訊息的三種方式
chrome.runtime.sendMessage。 一次性。它會送達每一個帶有 runtime.onMessage 監聽器的擴充功能情境,也就是 service worker、popup、options 頁面,以及任何其他已開啟的擴充功能頁面。它不會送達 content script。
chrome.tabs.sendMessage。 一次性,瞄準一個分頁。從 service worker 或另一個擴充功能頁面呼叫它,去送達那個分頁裡的 content script,還可以透過 frameId 只送達一個 frame。content script 必須已經在那裡執行。
chrome.runtime.connect 與 chrome.tabs.connect。 一個保持開啟的具名 port。兩端可以隨時投遞訊息,兩端也都透過 port.onDisconnect 得知拆除。
在你自己的情境之間傳訊息不需要額外權限。它需要的是對面有一個活著的監聽器。
非同步回應規則
runtime.onMessage 監聽器預設是同步回覆的。它一回傳,通道就關閉,之後再呼叫 sendResponse 會被丟棄。回傳 true 才是讓通道保持開啟的做法:
async 監聽器函式回傳的是 Promise,而 Promise 不是 true,因此在 Chromium 上,一個稍後才呼叫 sendResponse 的 async 監聽器仍然會關閉通道。請像上面那樣在監聽器內部做非同步工作並回傳 true。在呼叫端這一側,省略回呼時 chrome.runtime.sendMessage 會回傳 Promise,所以那裡直接 await 即可,與以上這些無關。
給每則訊息一個明確的 type,在處理之前驗證酬載,並在做特權工作之前檢查 sender。content script 是你的程式碼,但它轉發的資料來自你無法控制的頁面。
各瀏覽器差異
在 Safari 上,在你授予擴充功能網站存取權之前 content script 不會執行,因此在那之前
tabs.sendMessage 沒有接收方。請見 Safari。
如果你的原始碼是以 browser.* 撰寫,同時也要建置 Chromium 目標,請傳入 --polyfill。請見 跨瀏覽器相容。
你會看到的主控台錯誤
把你看到的那一行複製去搜尋。每一行對應一個原因。Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
沒有任何一方在監聽。要嘛目標情境沒有 runtime.onMessage 監聽器,要嘛對 tabs.sendMessage 而言,還沒有 content script 被注入那個分頁。在擴充功能載入之前就已開啟的分頁在重新載入前沒有 content script,而像 chrome:// 或擴充功能商店這樣的受限頁面永遠不會有。請先用 chrome.scripting.executeScript 注入,或者處理這次失敗,做法請見 在執行階段注入腳本。
Unchecked runtime.lastError: The message port closed before a response was received.
監聽器收到了訊息,卻在沒有讓通道保持開啟的情況下回傳了。請從監聽器回傳 true,或者同步作答。
Uncaught Error: Extension context invalidated.
擴充功能重新載入時,舊的 content script 還在頁面裡執行。那段程式碼現在指向一個不再存在的執行階段,它發出的每個 chrome.* 呼叫都會擲出錯誤。請重新載入分頁。在 extension dev 期間,這發生在一次被歸類為完整重新載入的變更之後,重新載入與 HMR 對此有說明。
Attempting to use a disconnected port object
在 onDisconnect 觸發之後,仍有人向這個 port 投遞訊息。請在 onDisconnect 處理常式裡清掉你的參照,需要時再開啟一個新的 port。
Extension.js 的做法
Extension.js 編譯呼叫這些 API 的程式碼,並不包裝它們。協定是你自己的。工具鏈改變的是開發循環:- 一次被歸類為
content-scripts的變更會重新注入受影響的項目並拆除上一次的掛載,因此監聽器是被取代而不是被疊加。一次被歸類為full的變更會重新載入擴充功能,而重新載入前就留在頁面上的 content script 會變成上面那種Extension context invalidated情況。請重新載入分頁。 - 在
extension dev之下,從scripts/資料夾注入的腳本會在編輯後被重播,因此你在被注入腳本裡開啟的 port 會由新的副本重新開啟。請見 特殊資料夾。 - service worker 與 content script 的主控台輸出會被轉發到同一個通道,因此跨越邊界的訊息更容易在單一串流裡追蹤。

