iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
Modern Web

你知道這是什麼嗎? Chrome Extension MV3 With Vite系列 第 14

# 你知道這是什麼嗎? Chrome extension MV3 With Vite - Day 14 資料的傳遞 Message Passing (上)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20220914/20139636vff8VoqMft.jpg

Hi Dai Gei Ho~ 我是Winnie ~

今天文章已經來到了14篇,而在開始進入今天主題之前,我想來回顧一下前面13篇文章的內容。

在前面文章中,我們介紹了 Chrome Extension 的 運作 主要透過 Manifest 設定檔來定義,其中 Background 為 Extension 中背後執行腳本,與 JavaScript 主執行緒的不同,其執行緒主要透過事件驅動來運行,所以不能直接操作頁面中的DOM元素,只能透過事件監聽 透過 訊息的傳遞 與 Content Script 溝通, 進而操作頁面的 內容。

那 Background 、 Content Script 與 Popup 要如何相互傳遞資料呢 ?

在這部分,我覺得可以依照 資料狀態是否需要被長存,來分為兩種方式:

  • Message Passing 訊息傳遞
  • Storage 存取

首先,在此篇文章中我們將先針對 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.connecttabs.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"})
   }
 }
});

關於 Port 的 生命週期

那要如何知道Connect 何時被斷開呢 ?

當其中一端使用 runtime.Port.disconnect 結束連結或是當前頁面被關閉,就會觸發生命週期的結束。

好了,前面說了這麼多~ 讓我們來實際看看Demo:

在這邊我把 ContentScript 與 Popup 相互傳遞資料的方法以對話式來呈現,以方便看到兩端的結果,但想像是美好的,假如實際要透過 Extension 來實現聊天室的功能還是不太合適的,還是建議可以socket 的方式來實現

Popup to Content Script:

當從 Popup 傳遞資料 給 Content Script時,需指定特定的頁籤ID,才能傳送給對頁面,當另一端接收到訊息可以透過sendResponse回應。

ContentScript to Popup :

同時,如果要從 瀏覽器端回傳就比較單純了,就像前面說的 只需透過chrome.runtime.sendMessage傳遞即可。

以上就是關於 訊息傳遞 一次性請求 與 長時間傳遞 的上半篇介紹,而在明天文章中,將針對 跨套件傳遞訊息 來進行應用介紹(每天都寫不完啦

那今天文章先到這邊了,謝謝願意花時間看此篇文章的你,如果文章有錯誤的地方,再麻煩不吝嗇的給予指教,感謝!!

今日有感而發:
逮補 -> 待補
先聽首歌吧 -> 房東的貓 - 簡情歌


上一篇
你知道這是什麼嗎? Chrome extension MV3 With Vite - Day 13 Option Page
下一篇
你知道這是什麼嗎? Chrome extension MV3 With Vite - Day 15 Message Passing跨Extension間的傳遞 (下)
系列文
你知道這是什麼嗎? Chrome Extension MV3 With Vite30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
歪歪
iT邦新手 3 級 ‧ 2023-02-22 15:00:13

請問一下,假設我的需求是popup.js設定完參數點選確認時,要暫時先將這個值保存下來,等到我切到我要執行的頁面時他會自動把值帶到我指定的欄位,這個暫存的動作應該要怎麼做?

我要留言

立即登入留言