我正在弄乱(试图学习)如何制作Chrome扩展程序。现在我只是制作超级简单的一个,它计算页面上某个单词的实例。我有这部分工作。
我想要做的是将此信息发送到流行音乐,以便我可以用它来做其他一些事情。
这是我到目前为止所做的:
的manifest.json
{
"manifest_version": 2,
"name": "WeedKiller",
"description": "Totally serious $100% legit extension",
"version": "0.1",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"permissions":[
"tabs",
"storage"
],
"browser_action": {
"default_icon": "icon.png",
"default_title": "WeedKiller",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"content.js"
],
"run_at": "document_end"
}
]
}
content.js
var elements = document.getElementsByTagName('*');
var count = 0;
function tokeCounter(){
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
for (var j = 0; j < element.childNodes.length; j++) {
var node = element.childNodes[j];
if (node.nodeType === 3) {
var text = node.nodeValue;
if(text == '420'){
count++;
}
var replacedText = text.replace(/420/, '+1');
if (replacedText !== text) {
element.replaceChild(document.createTextNode(replacedText), node);
}
}
}
}
}
tokeCounter();
所以我想要发生的是将count
变量发送到弹出窗口,以便我可以在那里使用它。
我环顾四周,发现我需要对chrome.runtime.sendMessage
做一些事情。
我有,所以我将这一行添加到content.js的末尾:
chrome.runtime.sendMessage(count);
然后在background.js中:
chrome.runtime.onMessage.addListener(
function(response, sender, sendResponse){
temp = response;
}
);
我有点困在这里,因为我不确定如何将此信息发送到弹出窗口并使用它。
答案 0 :(得分:38)
正如您已经注意到的那样,在关闭弹出窗口时,您无法直接将数据发送到弹出窗口。因此,您要将数据发送到后台页面。
然后,当打开弹出窗口时,你想要那里的数据。那么,有哪些选择?
请注意:这个答案首先会给出错误的建议,然后对其进行改进。由于OP正在学习,因此展示思维过程和道路颠簸是很重要的。
首先想到的解决方案如下:再次使用Messaging询问后台页面。 预警:这不起作用或效果不佳
首先,确定可以有不同类型的消息。修改您当前的消息代码:
// content.js
chrome.runtime.sendMessage({type: "setCount", count: count});
// background.js
chrome.runtime.onMessage.addListener(
function(message, sender, sendResponse) {
switch(message.type) {
case "setCount":
temp = message.count;
break;
default:
console.error("Unrecognised message: ", message);
}
}
);
现在,理论上你可以在弹出窗口中询问:
// popup.js
chrome.runtime.sendMessage({type: "getCount"}, function(count) {
if(typeof count == "undefined") {
// That's kind of bad
} else {
// Use count
}
});
// background.js
chrome.runtime.onMessage.addListener(
function(message, sender, sendResponse) {
switch(message.type) {
case "setCount":
temp = message.count;
break;
case "getCount":
sendResponse(temp);
break;
default:
console.error("Unrecognised message: ", message);
}
}
);
现在,这有什么问题?
temp
的生命周期是什么?您已明确声明"persistent": false
in your manifest。因此,可以随时卸载后台页面,擦除状态,例如temp
。
您可以使用"persistent": true
进行修复,但请继续阅读。
您希望看到哪个标签计数? temp
将写入最后一个数据,这可能不是当前标签。
你可以通过保留标签(看我在那里做了什么?)来修复它,在哪个标签上发送数据,例如使用:
// background.js
/* ... */
case "setCount":
temp[sender.tab.id] = message.count;
break;
case "getCount":
sendResponse(temp[message.id]);
break;
// popup.js
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// tabs is a single-element array after this filtering
chrome.runtime.sendMessage({type: "getCount", id: tabs[0].id}, function(count) {
/* ... */
});
});
虽然做了很多工作,但不是吗? 在修复1后,此解决方案适用于非特定于标签的数据。
要考虑的下一个改进:我们是否需要背景页面来存储结果?毕竟,chrome.storage
is a thing;它是所有扩展脚本(包括内容脚本)都可以访问的持久存储。
这会将背景(和消息传递)从图片中删除:
// content.js
chrome.storage.local.set({count: count});
// popup.js
chrome.storage.local.get("count", function(data) {
if(typeof data.count == "undefined") {
// That's kind of bad
} else {
// Use data.count
}
});
这看起来更干净,完全绕过了上面的问题1,但问题2变得更加棘手。 You can't directly set/read存储中的count[id]
之类的内容,您需要阅读count
,修改并将其写回。它会变得缓慢而混乱。
除此之外,内容脚本并不真正了解其标签ID;你需要发短信给背景才能学习它。啊。不漂亮。 同样,对于非特定于标签的数据,这是一个很好的解决方案。
然后问下一个问题:为什么我们甚至需要一个中心位置来存储(特定于标签的)结果?内容脚本的生命周期是页面的生命周期。您可以随时直接询问内容脚本。包括弹出窗口。
等等,等等,你是不是说在最顶层你不能向弹出窗口发送数据?嗯,是的,有点:当你不知道它是否在听。但是如果弹出窗口要求,那么它必须准备好得到响应,不是吗?
所以,让我们颠倒内容脚本逻辑。而不是立即发送数据,等待并听取请求:
chrome.runtime.onMessage.addListener(
function(message, sender, sendResponse) {
switch(message.type) {
case "getCount":
sendResponse(count);
break;
default:
console.error("Unrecognised message: ", message);
}
}
);
然后,在弹出窗口中,我们需要查询包含内容脚本的选项卡。它是一个不同的消息传递功能,我们必须指定选项卡ID。
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "getCount"}, function(count) {
/* ... */
});
});
现在更清洁了。问题2已经解决:我们查询了我们想要听到的标签。问题1似乎得到了解决:只要脚本计算了我们需要的东西,它就可以回答。
请注意,作为最终的复杂功能,内容脚本并不总是在您期望它们时注入:它们仅在扩展(重新)加载后才开始在导航上激活。这里an answer非常详细地解释了这一点。如果你愿意,它可以解决,但现在只是它的代码路径:
function(count) {
if(typeof count == "undefined") {
// That's kind of bad
if(chrome.runtime.lastError) {
// We couldn't talk to the content script, probably it's not there
}
} else {
// Use count
}
}