浏览器操作,后台脚本和chrome扩展的内容脚本之间的通信的上下文和方法?

mow*_*ker 28 javascript google-chrome google-chrome-extension

我认为chrome扩展总体而言非常简单且非常强大,但总有一件事让我感到困惑的是尝试在代码可能运行的各种脚本之间进行通信.从浏览器操作的"default_popup"页面引用时会运行代码,"后台"的"脚本"属性中的代码和内容脚本.

在什么情况下,这些类别中的脚本运行,以及每个类别如何与其他类别进行通信?

Rob*_*b W 67

三种不同的背景

作为Chrome扩展程序开发人员,您可以区分三种不同的环境.

  1. 扩展程序代码,在您的Chrome扩展程序中运行
  2. 内容脚本,在选项卡的进程中运行.
  3. 在选项卡的进程中运行的非扩展代码(由内容脚本注入).

请注意,<iframe src="chrome-extension://EXTENSIONID/page.htm">在非扩展页面中曾经被视为情况2(内容脚本),因为框架是在非特权选项卡进程中加载​​的.由于针对Chrome 56中的扩展程序启动了进程外iframe,因此这些页面由扩展程序处理,因此它们可能使用相同的完整扩展API集.行为的这种改变(允许扩展帧使用特权扩展API)是有意的.

window在扩展过程中访问对象

由于所有扩展代码都在同一进程中运行,因此它们可以访问彼此的全局window对象.此功能并不为人所知,但允许用户在同一扩展进程中直接操作JavaScript和DOM对象.通常最好不要使用此方法,而是使用消息传递 API代替.

// To access the `window` of a background page, use
var bgWindowObject = chrome.extension.getBackgroundPage();
// To access the `window` of an event or background page, use:
chrome.runtime.getBackgroundPage(function(bgWindowObject) {
    // Do something with `bgWindow` if you want
});

// To access the `window` of the badge's popup page (only if it's open!!!), use
var popupWindowObject = chrome.extension.getViews({type:'popup'})[0];

// To access the `window` of the options page (called /options.html), use
var allWindowObjects = chrome.extension.getViews({type:'tab'});
var popupWindowObjects = allWindowObjects.filter(function(windowObject) {
    return windowObject.location.pathname == '/options.html';
});
// Example: Get the `window` object of the first options page:
var popupWindowObject = popupWindowObjects[0];
Run Code Online (Sandbox Code Playgroud)

为了简化这一部分,我有意将代码示例限制为访问其他全局window对象的演示.您可以使用这些方法来定义全局方法,设置全局变量,调用全局函数等
......只要页面是打开的.有人认为弹出窗口window始终可用.事实并非如此,当弹出窗口关闭时,全局对象被处理掉!

通过消息传递的通信

消息通道始终有两个端点:发送方和接收方.
要成为接收者,请使用该chrome.runtime.onMessage.addListener方法绑定事件侦听器.这可以通过扩展代码和内容脚本来完成.

要在扩展程序中传递邮件,请使用chrome.runtime.sendMessage.如果要将消息发送到另一个选项卡,请致电chrome.tabs.sendMessage.通过包含integer(tabId)作为其第一个参数来指定目标选项卡.请注意,后台页面只能向一个选项卡发送消息.要访问所有选项卡,必须为每个选项卡调用该方法.例如:

chrome.tabs.query({}, function(tabs) {
    for (var i=0; i<tabs.length; i++) {
        chrome.tabs.sendMessage(tabs[i].id, "some message");
    }
});
Run Code Online (Sandbox Code Playgroud)

内容脚本只能调用chrome.runtime.sendMessage以向扩展代码发送消息.如果要将消息从内容脚本发送到另一个内容脚本,则需要一个后台/事件页面,该页面接收消息并将其发送到所需的选项卡.看到这个答案以获取示例.

这些sendMessage方法接受一个可选函数,该函数作为onMessage事件的第三个参数被接收.

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message === 'message') sendResponse('the response');
});
chrome.runtime.sendMessage('message', function(response) {
    console('sendResponse was called with: ' + response);
});
Run Code Online (Sandbox Code Playgroud)

前面的示例显示了明显的行为.当您想要异步发送响应时,事情会变得棘手,例如,如果您想执行AJAX请求来获取某些数据.当onMessage函数返回而没有调用时sendResponse,Chrome将立即调用sendResponse.由于sendResponse只能调用一次,您将收到以下错误:

无法发送响应:如果要在侦听器返回后发送响应,则chrome.runtime.onMessage侦听器必须返回true(消息由扩展名EXTENSION ID HERE发送)

按照错误建议执行操作,return true;在onMessage事件侦听器中添加:

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    setTimeout(function() { // Example: asynchronous invocation of sendResponse
        sendResponse('async response');
    }, 200);
    return true;
});
Run Code Online (Sandbox Code Playgroud)

我已经解释了本节中简单的一次性消息传递的实际应用.如果您想了解有关长期消息通道或跨扩展消息的更多信息,请阅读官方文档中教程.

传递API的消息经历了多次名称更改.如果您阅读旧示例,请记住这一点.可以在此处找到历史记录和兼容性说明.

内容脚本与页面之间的通信

可以与页面通信.Apsillers创建了一个很好的答案,解释了如何在(非扩展)页面和内容脚本之间建立通信通道.在网站上调用浏览器扩展,阅读他的回答.

apsiller的方法优于文档中的方法的优点是使用自定义事件.该文档用于window.postMessage向页面发送消息,但这可能会导致与编码错误的页面发生冲突,这些页面不会发生消息事件.

  • 哦,这个答案值得接受,还有更多的赞成.很好解释. (4认同)