我认为chrome扩展总体而言非常简单且非常强大,但总有一件事让我感到困惑的是尝试在代码可能运行的各种脚本之间进行通信。当从“default_popup”页面引用时,会运行代码。浏览器操作,“后台”的“脚本”属性中的代码和内容脚本。
这些类别中的脚本在什么情况下运行,以及每种类型如何与其他类别进行通信?
答案 0 :(得分:66)
作为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
对象的演示。您可以使用这些方法来定义全局方法,设置全局变量,调用全局函数等
...只要页面打开。弹出窗口window
始终可用的人thought。事实并非如此,当弹出窗口关闭时,全局对象被处理掉!
消息通道始终有两个端点:发送方和接收方
要成为接收者,请使用chrome.runtime.onMessage.addListener
方法绑定事件侦听器。这可以通过扩展代码和内容脚本来完成。
要在扩展程序中传递消息,请使用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
只能调用一次,因此会收到以下错误:
无法发送响应:如果您想在侦听器返回后发送响应,则chrome.runtime.onMessage侦听器必须返回true(消息是通过扩展 EXTENSION ID HERE 发送的)
按照错误建议,在您的onMessage事件监听器中添加return true;
:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
setTimeout(function() { // Example: asynchronous invocation of sendResponse
sendResponse('async response');
}, 200);
return true;
});
我已经解释了本节中简单的一次性消息传递的实际应用。如果您想了解有关长期消息频道或跨扩展消息的更多信息,请阅读tutorial from the official documentation。
消息传递API经历了多次名称更改。如果您阅读旧示例,请记住这一点。可以找到历史记录和兼容性说明here。
可以与页面进行通信。 Apsillers创建了一个很好的答案,解释了如何在(非扩展)页面和内容脚本之间建立通信通道。在Can a site invoke a browser extension?阅读他的回答。
apsiller方法优于one from the documentation的优点是使用了自定义事件。该文档使用window.postMessage
向页面发送消息,但这可能会导致与编码错误的页面发生冲突,这些页面不会发生消息事件。
答案 1 :(得分:3)
Google文档包含所有内容,但很难将所有信息整合在一起。脚本有两种主要类型:
1.后台脚本可以完全访问Chrome api,但无法与目标网页进行交互
2.内容脚本可以相互交互,也可以与网页的DOM(但不是它的脚本,它是隔离的)进行交互,但只能有限地访问Chrome api。
每当您加载新页面时都会运行(除非您使用“匹配”来限制内容脚本的运行位置)。
您可以通过message passing在两者之间进行通信。从内容脚本比从后台脚本更容易实现这一点,因为您需要知道后者的选项卡ID。
其他脚本(browserAction.js
,pageAction.js
,optionsPage.js
)仅在打开相应的html页面时运行(就像您在浏览器窗口中打开网页一样,这就是你'真的这么做'。它们与限制和能力中的背景脚本类似。
尽量避免与页面脚本进行交互。我所知道的最好的方法是通过共享DOM进行交互(字面上在html注释中编写javascript代码)。但是您的扩展程序的目标不是为此设计的,因此您必须将自己的脚本包含在网页中。使用内容脚本将脚本元素写入文档(其src
为
chrome.extension.getURL("myscript.js")
,
而且你需要有
"web_accessible_resources": ["myscript.js"]
在你的清单中。
答案 2 :(得分:-1)
我已经有一段时间不得不处理chrome的扩展了。我记得在事情发生之前,这是一场相当艰苦的斗争。为了使您的扩展程序能够与浏览器进行通信,您可以轻松地使用您的javascript /后台文件,并与您需要使用chrome.tabs.executeScript
的网页进行通信,但这非常棘手,并且可能是一个非常痛苦的屁股。我建议你带上google's tour on extensions并给他们的api一个非常好的俯视,一切都在那里!祝你好运,希望这个答案对你有所帮助! :P