一個複雜的 Chrome 擴展程式通常由 `content_scripts`,`background`,`action popup`,`side panel`,`options page`,`devtools` 等部分組成,這些部分所負責的功能各不相同,所處的運行環境各不相同,所能訪問的 `chro... ...
大家好,我是 dom 哥。這是我關於 Chrome 擴展開發的系列文章,感興趣的可以 點個小星星。
一個複雜的 Chrome 擴展程式通常由 content_scripts
,background
,action popup
,side panel
,options page
,devtools
等部分組成,這些部分所負責的功能各不相同,所處的運行環境各不相同,所能訪問的 chrome.*
API 也各不相同,也因此經常需要通信告訴對方需要做什麼。
下麵是我畫的一張圖,簡單說明各部分關係:
這些花花綠綠的部分各自運行在不同的環境中,往往需要相互通信,Chrome 為我們提供了兩種通信方式:
- 一種是一次性請求(one-time requests),一次只能發一條消息,類似於手機發簡訊,跟 HTTP 請求很像。
- 一種是長期連接(long-lived connections),允許發送多條消息,類似於手機打電話,跟 Websocket 連接很像。
接下來就詳細說說這兩種通信方式。
一次性請求(one-time requests)
如果要向擴展程式的另一部分發送一條消息,有兩個 API 可供調用:
chrome.runtime.sendMessage(extensionId?, message)
chrome.tabs.sendMessage(tabId, message)
從函數簽名很容易看出來,一個是向擴展程式的各個部分發消息的,另一個是給某個瀏覽器的某個頁簽發消息的。
為什麼設計兩個 API?這是因為 content_scripts
是一個很獨特的存在!
先說說瀏覽器的的工作原理。
瀏覽器的每個頁簽都是單獨的線程。每個頁簽運行在與其他頁簽或擴展相隔離的獨立線程中。如下圖所示
我們在每個頁簽中打開頁面。content_scripts
是一個很特殊的存在!作為被註入到頁面的腳本,它的生命周期跟隨頁面。而擴展程式的其他部分,都有自己的生命周期!如果你在各個部分查看它們的 location
,就會發現,只有 content_scripts
的 origin 是頁面的 url 一樣,而其它部分的 location.origin 都是 chrome://your-extention-id
。
知道了 content_scripts
的特殊性後,那麼這兩個 API 就很好理解了。
chrome.runtime.sendMessage()
是給擴展程式發消息的,它的第一個參數是一個可選的 extensionId,意味著不但可以給自身擴展程式發消息,還能給別的擴展程式發消息,它發送的消息可以被擴展的任一部分接收到,包括 background
,action popup
,side panel
,options page
,devtools
等等,除了 content_scripts
!!!
那麼想給 content_scripts
發送消息怎麼辦呢???
chrome.tabs.sendMessage()
就是專門用來給 content_scripts
發消息的!值得註意的是,想要給 content_scripts
發消息需要指定 tabId,也就是需要指明給哪個頁簽下的頁面的 content_scripts
發消息。這個設計很好,因為每個頁簽的頁面都運行了一份 content_scripts
,這就避免了無關的頁面接收到消息。
發送消息搞定啦,有方法發送就得有方法接收才行啊。
接收消息的方法只有一個 API:
chrome.runtime.onMessage.addListener(
(message, sender, sendResponse) => boolean | undefined
)
在擴展程式的任意部分(包括 content_scripts
)都是用這個接收消息。這很方便。
長期連接(long-lived connections)
長期鏈接的 API 和一次性消息的 API 是相互對應的。
要創建一個可重覆使用的長期消息傳遞通道,有兩個 API 可以調用:
chrome.runtime.connect(extensionId?, connectInfo?): Port
chrome.tabs.connect(tabId, connectInfo?): Port
這兩個 API 的設計和一次性消息的一樣。
chrome.runtime.connect()
用於和擴展程式的任一部分建立消息通道,除了 content_scripts
!!!
而 chrome.tabs.connect()
是專門用來和 content_scripts
建立消息通道噠!
chrome.{runtime,tabs}.connect()
返回的是一個 Port 對象。Port 就是被設計用來在擴展程式的不同部分之間進行雙向通信的一個介面。
覺得不錯可以 點個小星星 支持一下哦