在我的 Chrome 扩展中,内容脚本应该在页面加载时从后台脚本接收消息。但是当我打开一个新选项卡并输入 URL https://example.com 时,它没有收到消息。但是,它在页面重新加载时起作用。 是因为页面是从 chrome://newtab 加载的吗?
// background script
chrome.runtime.onMessage.addListener(function (message, sender) {
if (message.action === "NOTIFY_CONTENT_SCRIPT_LOADED") {
console.log(`### ACK content script in page: ${message.pageURL} and tab: ${sender.tab.url}`);
chrome.tabs.sendMessage(sender.tab.id, { action: "CHANGE_BG" }, () => {
console.log("### ACK changed BG color of client page");
// do more stuff
});
}
});
// content script
chrome.runtime.onMessage.addListener((message, _, sendResponse) => {
if (message.action === "CHANGE_BG") { // <---- This is not received on first page load
document.body.style.background = "yellow";
sendResponse();
}
});
console.log("### Notifying background from page: " + location.href);
chrome.runtime.sendMessage({ action: "NOTIFY_CONTENT_SCRIPT_LOADED", pageURL: location.href });
来自后台脚本的控制台日志:
### ACK content script in page: https://example.com/ and tab: chrome://newtab/
请注意,
sender.tab.url
是chrome://newtab/
而不是https://example.com/
。
这是间歇性发生的。也就是说,在某些情况下不会发生:
我创建了一个具有相同问题的示例扩展:https://github.com/lazyvab/chrome-newtab-issue
编辑:尝试了评论中的建议 -
如果有多条消息,请不要使用sendMessage,而是切换到chrome.runtime.connect。
// content script
const bgPort = chrome.runtime.connect();
bgPort.onMessage.addListener((msg) => {
console.log("### Received BG msg", msg);
});
// background script
chrome.runtime.onConnect.addListener((port) => {
port.onDisconnect.addListener((...args) => {
console.log("### disconnected port on tab", tabId, ...args);
});
console.log("### BG: sending ping to tab", tabId);
port.postMessage({ action: "ping" });
});
我在新选项卡中打开 https://example.com,这是结果。
从截图中可以看出,网站加载后端口立即断开连接,后台脚本无法再次使用该端口发送消息。
在预渲染页面的情况下,我最终可以通过在内容脚本和后台脚本之间建立长期端口连接来解决这个问题。
// background.js
let clientPort = null;
let clientLoadSubscribers = [];
chrome.runtime.onConnect.addListener((port) => {
const tabId = port.sender.tab.id;
if (port.sender.documentLifecycle === "active") {
clientPort = port;
clientLoadSubscribers.forEach((subscriber) => subscriber());
clientLoadSubscribers = [];
}
port.onDisconnect.addListener(() => {
clientPort = null;
chrome.tabs.executeScript(
tabId,
{
code: "chrome.runtime.connect()",
},
() => {
if (chrome.runtime.lastError) {
// ignore
}
}
);
});
});
const sendMessageToClient = (tabId, ...restArgs) => {
const send = () => chrome.tabs.sendMessage(tabId, ...restArgs);
if (clientPort) {
send();
} else {
clientLoadSubscribers.push(send);
}
};
// content script
chrome.runtime.connect();
这样我就可以使用
sendMessageToClient()
可靠地将消息从后台发送到内容脚本。