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'});

 

posted @ 2021-12-29 15:18  冰糖葫芦很乖  阅读(6628)  评论(0编辑  收藏  举报