我认为 chrome 扩展总体上非常简单且非常强大,但总是让我感到困惑的一件事是尝试在可能运行代码的各种脚本之间进行通信。当从浏览器操作的“default_popup”页面引用时,就会运行代码,“背景”的“脚本”属性中的代码和内容脚本。
这些类别中的脚本在什么上下文中运行,每个脚本如何与其他脚本通信?
最佳答案
三种不同的语境
作为 Chrome 扩展开发者,您可以区分三种不同的环境。
请注意
<iframe src="chrome-extension://EXTENSIONID/page.htm">
在非扩展页面中,过去被视为案例 2(内容脚本),因为框架是在非特权选项卡进程中加载的。自 out-of-process iframes在 Chrome 56 中为扩展启动,这些页面由扩展进程处理,因此它们可能使用相同的全套扩展 API。此 change in behavior (allowing extension frames to use privileged extension APIs) is intentional .访问
window
扩展过程中的对象因为所有的扩展代码都运行在同一个进程中,所以它们可以互相访问全局
window
目的。这一特性并不广为人知,但允许在同一个扩展进程中直接操作 JavaScript 和 DOM 对象。通常最好不要使用这种方法,而是使用 message passing取而代之的是 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];
为了使本节简短,我有意将代码示例限制为访问其他全局
window
的演示。对象。您可以使用这些方法来定义全局方法、设置全局变量、调用全局函数等。... 前提是页面是打开的。有人thought弹出窗口的
window
始终可用。这不是真的,当弹出窗口关闭时,全局对象就被释放了!交流来自 message passing
消息 channel 总是有两端:发送者和接收者。
要成为接收者,请使用
chrome.runtime.onMessage.addListener
绑定(bind)事件监听器方法。这可以通过扩展代码和内容脚本来完成。要在扩展中传递消息,请使用
chrome.runtime.sendMessage
.如果您想向另一个选项卡发送消息,请调用 chrome.tabs.sendMessage
.目标选项卡是通过包含一个整数 ( tabId
) 作为其第一个参数来指定的。请注意,后台页面只能向一个选项卡发送消息。要访问所有选项卡,必须为每个选项卡调用该方法。例如:chrome.tabs.query({}, function(tabs) {
for (var i=0; i<tabs.length; i++) {
chrome.tabs.sendMessage(tabs[i].id, "some message");
}
});
内容脚本只能调用
chrome.runtime.sendMessage
向分机代码发送消息。如果您想从一个内容脚本向另一个内容脚本发送消息,则需要一个背景/事件页面,它接收一条消息并将其发送到所需的选项卡。见 this answer举个例子。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);
});
前面的示例显示了明显的行为。当您想要异步发送响应时,事情会变得更加棘手,例如,如果您想要执行 AJAX 请求以获取一些数据。当
onMessage
函数在没有调用的情况下返回 sendResponse
,Chrome 会立即调用 sendResponse
.自 sendResponse
只能调用一次,您将收到以下错误:Could not send response: The chrome.runtime.onMessage listener must return true if you want to send a response after the listener returns (message was sent by extension 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;
});
在本节中,我已经解释了简单的一次性消息传递的实际应用。如果您想了解有关长期消息 channel 或跨扩展消息传递的更多信息,请阅读 tutorial from the official documentation .
消息传递 API 经历了几次名称更改。如果您阅读旧示例,请记住这一点。历史和兼容性说明可以在 here 中找到.
内容脚本和页面之间的通信
可以与页面进行通信。 Apsillers 创建了一个很好的答案,它解释了如何在(非扩展)页面和内容脚本之间建立通信 channel 。阅读他的回答 Can a site invoke a browser extension? .
apsiller 方法相对于 one from the documentation 的优势是使用了自定义事件。文档使用
window.postMessage
向页面发送消息,但这可能会导致与不期望消息事件的错误编码页面发生冲突。
关于javascript - chrome 扩展的浏览器 Action 、后台脚本和内容脚本之间通信的上下文和方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17246133/