Hi Dai Gei Ho~ 我是Winnie ~
今天文章已經來到了14篇,而在開始進入今天主題之前,我想來回顧一下前面13篇文章的內容。
在前面文章中,我們介紹了 Chrome Extension 的 運作 主要透過 Manifest 設定檔來定義,其中 Background 為 Extension 中背後執行腳本,與 JavaScript 主執行緒的不同,其執行緒主要透過事件驅動來運行,所以不能直接操作頁面中的DOM元素,只能透過事件監聽 透過 訊息的傳遞 與 Content Script 溝通, 進而操作頁面的 內容。
那 Background 、 Content Script 與 Popup 要如何相互傳遞資料呢 ?
在這部分,我覺得可以依照 資料狀態是否需要被長存,來分為兩種方式:
首先,在此篇文章中我們將先針對 Message Passing 中的 一次性傳遞
與 長時間傳遞
應用方式來介紹。
如果資料不需常時效性的被更新傳遞,以這類型的短期性傳遞資料為需求來說,我們可以透過chrome.runtime.sendMessage()
或者 tab.runtime,sendMessagi()
方法來進行資料傳遞。
小提醒: 傳遞的資料形式 需以 JSON物件格式 來進行傳遞。
Background 傳遞 Content Script :
因為開啟視窗不只一個,所以在Background 背後事件需知道所要傳遞的頁面,透過第一個參數指定 Tab ID 的形式來將內容傳遞到特定頁面中。
// Background to Content Script
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { from: 'background.js' }, (response)=>{
console.log(response.from);
});
});
Content Script 傳遞 background :
相較於 Background 傳遞 Content Script 方法,由 瀏覽器端 傳遞資料 給 Extension 端 就較為單純了,因為存在於 Extension 中的Context 只有一個,所以不會有傳錯對象的問題。
//Content Script to Background
chrome.runtime.sendMessage({ from: 'Content Script' }, (response) => {
console.log('response from:', response);
// 此處接收方如有回傳的資料,可以在此接收。
});
而在接收訊息的部分,就沒這麼麻煩惹,不管是 Content Script 或者 Background 中只要設置一個chrome.runtime.onMessage.addListener
即可接收訊息,如有需回傳資料,透過 sendResponse 即可回傳。
同時因為 sendResponse 方法僅在同步使用時有效,如果想要非同步使用,需在onMessage監聽事件
的CallBack function 中return true
,即可使用。
這邊需注意:
sendMessage如果沒有處理程序返回 true 或者sendResponse函數的callback將被自動調用。
//background.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.from === 'popup.js') {
// 可以在 sendMessage 的 callback 中取得,此 sendResponse 的內容
// 需要注意若在多個地方呼叫同時呼叫 sendResponse,將只會收到一個
sendResponse({
from: 'background.js',
});
}
});
溫腥提醒:
如果同時多個頁面監聽onMessage事件,此時只有 第一個監聽事件調用sendResponse()事件有效,其餘的會被忽略。
接著是 長時間連結,假如資料需時常被雙方更新與相互使用時,我們可以使用 runtime.connect
或 tabs.connect
來創建 long-lived channel,當建立一個連結時會得到一個 runtime.Port 物件,此時可以透過此物件來發送和接收訊息,讓資料隨時保持共享狀態。
以下面是從 content script 發起長時間連結並發送接收訊息為例:
一開始,需透過chrome.runtime.connect
方法來創建一個 Port 進行連接。
此時這邊的Connect連結名稱可以自訂義,以便接收端判斷發送從哪裡傳遞的。
const port =chrome.runtime.connect({name: "contentPort"})
接著可以透過runtime.connect中的 postMessage方法來傳遞訊息。
port.postMessage({from: "content script"})
而監聽訊息的傳遞,可以透過設置port.onMessage.addListener
來接收訊息。
port.onMessage.addListener((msg)=>{
if (msg.from === "content script"){
port.postMessage({data: data});
}
}
反之,如果要從Extension端發送請求到 content script 的做法也很類似,除了像一次性請求一樣需指定要連接Tab外,再來只需要將chrome.runtime.connect
它改成 tabs.connect
即可。
但以 Extension 中的接收端來說,與Content Script 頁面接收比較不相同的地方在於,當要處理Connect連接事件,需透過runtime.Connect
事件監聽,透過回傳port對進行資料的接收。
chrome.runtime.onConnect.addListener((port)=>{
if(port.name === "contentPort"){
port.onMessage.addListener((res)=>{
if (res.from === "content script"){
port.postMessage({from: "popup"})
}
}
});
那要如何知道Connect 何時被斷開呢 ?
當其中一端使用 runtime.Port.disconnect
結束連結或是當前頁面被關閉,就會觸發生命週期的結束。
好了,前面說了這麼多~ 讓我們來實際看看Demo:
在這邊我把 ContentScript 與 Popup 相互傳遞資料的方法以對話式來呈現,以方便看到兩端的結果,但想像是美好的,假如實際要透過 Extension 來實現聊天室的功能還是不太合適的,還是建議可以socket 的方式來實現
。
當從 Popup 傳遞資料 給 Content Script時,需指定特定的頁籤ID,才能傳送給對頁面,當另一端接收到訊息可以透過sendResponse
回應。
同時,如果要從 瀏覽器端回傳就比較單純了,就像前面說的 只需透過chrome.runtime.sendMessage
傳遞即可。
以上就是關於 訊息傳遞 一次性請求 與 長時間傳遞 的上半篇介紹,而在明天文章中,將針對 跨套件傳遞訊息 來進行應用介紹(每天都寫不完啦。
那今天文章先到這邊了,謝謝願意花時間看此篇文章的你,如果文章有錯誤的地方,再麻煩不吝嗇的給予指教,感謝!!
今日有感而發:
逮補 -> 待補
先聽首歌吧 -> 房東的貓 - 簡情歌
請問一下,假設我的需求是popup.js設定完參數點選確認時,要暫時先將這個值保存下來,等到我切到我要執行的頁面時他會自動把值帶到我指定的欄位,這個暫存的動作應該要怎麼做?