Chrome插件中 popup,background,contantscript消息傳遞機制


轉載來自:https://blog.csdn.net/summerxiachen/article/details/78698878

chrome 插件主要由三部分構成

 

 

 

1.popup
在用戶點擊擴展程序圖標時(下圖中的下載圖標),都可以設置彈出一個popup頁面。而這個頁面中自然是可以包含運行的js腳本的(比如就叫popup.js)。它會在每次點擊插件圖標——popup頁面彈出時,重新載入。


2.content_scripts
是會注入到Web頁面的JS文件,可以是多個,也可以對注入條件進行設置,也就是滿足什么條件,才會將這些js文件注入到當前web頁面中。
可以把這些注入的js 文件和網頁的個文件看成一個整體,相當於在你網頁中,寫入了這些js 代碼。這樣就可以對原來的web頁面進行操作了。

3 background 即插件運行的環境
可以是html+js, 也可以是單純的js
插件啟動后就常駐后台,只有一個。這類腳本是運行在瀏覽器后台的,注意它是與當前瀏覽頁面無關的。


在實際運行過程中
原始web+注入的的content_scripts=新的web頁面
當打開多個頁面時,就會存在多個新的web頁面。因為每個頁面都注入content_scripts。
那么在通信的時候,后台腳本或則popup頁面,怎么確定是與那個頁面進行消息交互呢,通過tabID
tab是什么呢?

上圖就有三個tab標簽,也就是在瀏覽器中打開的網頁對應着一個tab,圖中第二個和第三個雖然url相同,但tabid不一樣。

三個主要部分消息交互機制如下圖

通過上圖我們可以把交互消息分為三類:【A】【B】【C】

 

 

【A】直接發送消息一般是

//content_scripts——>background 例如

chrome.runtime.sendMessage(
    {
        greeting : message || '你好,我是content-script呀,我主動發消息給后台!'},
        function(response) {
        tip('收到來自后台的回復:' + response);
    }
);

發出方是主動發送消息,那么接收方必須時刻准備接受消息,才能保證及時接收到,所以接收方都是通過監聽這一動作來完成消息的接收

// 監聽消息

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)
{
    // code...
    sendResponse('我已收到你的消息:' +JSON.stringify(request));//做出回應
});

【B】發送消息一般是先獲取到tabID在發送消息

function getCurrentTabId(callback)
{
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
    {
        if(callback) callback(tabs.length ? tabs[0].id: null);
    });
}

function sendMessageToContentScript(message, callback)
{
    getCurrentTabId((tabId) =>
    {
        chrome.tabs.sendMessage(tabId, message, function(response)
        {
            if(callback) callback(response);
        });
    });
}
sendMessageToContentScript('你好,我是bg!', (response) => {
  if(response) alert('收到來自content-script的回復:'+response);
});

【C】popup調用后台腳本中的方法

var bg = chrome.extension.getBackgroundPage();
bg.test();//test()是background中的一個方法

通信詳細介紹

1.popup 和 background

popup可以直接調用background中的JS方法,也可以直接訪問background的DOM:

// background.js
function test()
{
    alert('我是background!');
}

// popup.js
var bg = chrome.extension.getBackgroundPage();
bg.test(); // 訪問bg的函數
alert(bg.document.body.innerHTML); // 訪問bg的DOM

至於background訪問popup如下(前提是popup已經打開):

var views = chrome.extension.getViews({type:'popup'});
if(views.length > 0) {
    console.log(views[0].location.href);
}

2.popup或者bg向content主動發送消息

background.js或者popup.js:

function sendMessageToContentScript(message, callback)
{
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
    {
        chrome.tabs.sendMessage(tabs[0].id, message, function(response)
        {
            if(callback) callback(response);
        });
    });
}
sendMessageToContentScript({cmd:'test', value:'你好,我是popup!'}, function(response)
{
    console.log('來自content的回復:'+response);
});

content-script.js接收:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)
{
    // console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");
    if(request.cmd == 'test') alert(request.value);
    sendResponse('我收到了你的消息!');
});

雙方通信直接發送的都是JSON對象,不是JSON字符串,所以無需解析,很方便(當然也可以直接發送字符串)。

網上有些老代碼中用的是chrome.extension.onMessage,沒有完全查清二者的區別(貌似是別名),但是建議統一使用chrome.runtime.onMessage

3.content-script主動發消息給后台

content-script.js:

chrome.runtime.sendMessage({greeting: '你好,我是content-script呀,我主動發消息給后台!'}, function(response) {
    console.log('收到來自后台的回復:' + response);
});

background.js 或者 popup.js:

// 監聽來自content-script的消息
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)
{
    console.log('收到來自content-script的消息:');
    console.log(request, sender, sendResponse);
    sendResponse('我是后台,我已收到你的消息:' + JSON.stringify(request));
});
  • content_scripts向popup主動發消息的前提是popup必須打開!否則需要利用background作中轉;
  • 果background和popup同時監聽,那么它們都可以同時收到消息但是只有一個可以sendResponse,一個先發送了,那么另外一個再發送就無效;

4.injected script和content-script

content-script和頁面內的腳本injected-script自然也屬於頁面內的腳本)之間唯一共享的東西就是頁面的DOM元素,有2種方法可以實現二者通訊:

  1. 可以通過window.postMessagewindow.addEventListener來實現二者消息通訊;
  2. 通過自定義DOM事件來實現;

 

第一種方法(推薦):

injected-script中:

window.postMessage({"test": '你好!'}, '*');

content script中:

window.addEventListener("message", function(e)
{
    console.log(e.data);
}, false);

第二種方法:

injected-script

var customEvent = document.createEvent('Event');
customEvent.initEvent('myCustomEvent', true, true);
function fireCustomEvent(data) {
    hiddenDiv = document.getElementById('myCustomEventDiv');
    hiddenDiv.innerText = data
    hiddenDiv.dispatchEvent(customEvent);
}
fireCustomEvent('你好,我是普通JS!');

content-script.js中:

var hiddenDiv = document.getElementById('myCustomEventDiv');
if(!hiddenDiv) {
    hiddenDiv = document.createElement('div');
    hiddenDiv.style.display = 'none';
    document.body.appendChild(hiddenDiv);
}
hiddenDiv.addEventListener('myCustomEvent', function() {
    var eventData = document.getElementById('myCustomEventDiv').innerText;
    console.log('收到自定義事件消息:' + eventData);
});

5.injected-script與popup通信

injected-script無法直接和popup通信,必須借助content-script作為中間人。

 

長連接和短鏈接

一個是短連接(chrome.tabs.sendMessagechrome.runtime.sendMessage),一個是長連接(chrome.tabs.connectchrome.runtime.connect)。

短連接的話就是擠牙膏一樣,我發送一下,你收到了再回復一下,如果對方不回復,你只能重新發,而長連接類似WebSocket會一直建立連接,雙方可以隨時互發消息。

popup.js:

getCurrentTabId((tabId) => {
    var port = chrome.tabs.connect(tabId, {name: 'test-connect'});
    port.postMessage({question: '你是誰啊?'});
    port.onMessage.addListener(function(msg) {
        alert('收到消息:'+msg.answer);
        if(msg.answer && msg.answer.startsWith('我是'))
        {
            port.postMessage({question: '哦,原來是你啊!'});
        }
    });
});

 

content-script.js:

// 監聽長連接
chrome.runtime.onConnect.addListener(function(port) {
    console.log(port);
    if(port.name == 'test-connect') {
        port.onMessage.addListener(function(msg) {
            console.log('收到長連接消息:', msg);
            if(msg.question == '你是誰啊?') port.postMessage({answer: '我是你爸!'});
        });
    }
});

動態注入或執行JS

雖然在backgroundpopup中無法直接訪問頁面DOM,但是可以通過chrome.tabs.executeScript來執行腳本,從而實現訪問web頁面的DOM(注意,這種方式也不能直接訪問頁面JS)。

示例manifest.json配置:

{
    "name": "動態JS注入演示",
    ...
    "permissions": [
        "tabs", "http://*/*", "https://*/*"
    ],
    ...
}

JS:

// 動態執行JS代碼
chrome.tabs.executeScript(tabId, {code: 'document.body.style.backgroundColor="red"'});
// 動態執行JS文件
chrome.tabs.executeScript(tabId, {file: 'some-script.js'});

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM