消息传递
这部分将从三个方面介绍消息传递:
- 插件内部的通讯
- 插件与插件之间的通讯
- 插件与页面之间的通讯
同时,穿插介绍background
、content
的特殊性
1. 插件内部的通讯
提示
background
的特殊性:
- 一个长期运行的后台脚本,它的生命周期与浏览器保持一致,即使没有打开任何
Tab
页,background
也会一直运行 - 没有生命周期的限制,所以常用于事件监听
- 无法使用
DOM
操作,因为没有DOM
环境
content
的特殊性:
- 一个独立于页面的脚本,它的生命周期与页面保持一致,刷新页面后,
content
会重新加载 - 因为它是在页面中运行的,所以可以直接操作页面的
DOM
危险
报错: Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist. 报错原因: sendMessage时,没有接收方。可能原因:
- 接收方没有注册
onMessage/onConnect
事件 - 接收方未加载,如
content
未加载、popup
未打开
background
接收处理消息、监听插件与浏览器的生命周期
// 接收来自插件内部的消息
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
console.log('🍄 ', message, sender, sendResponse);
});
// 建立长连接时触发
chrome.runtime.onConnect.addListener(function (port) {
// 监听 content,popup 发送的消息
port.onMessage.addListener(function (msg) {
console.log('🍄 ', port, msg);
if (port.name === 'xxx') {
port.postMessage({ from: 'background' });
}
});
});
// 监听安装事件
chrome.runtime.onInstalled.addListener(async (details) => {
console.log('🍄 ', details);
if (details.reason === 'install') {
await MessageStorage.setProperty({
install: {
from: 'background'
}
});
// 安装完成后打开新页面
chrome.tabs.create({
url: path
});
}
});
// 监听卸载事件
chrome.runtime.onUninstalled.addListener(async (details) => {
console.log('🍄 监听卸载事件', Date.now(), details);
if (details.reason === 'uninstall') {
// ...
}
});
// 监听启动事件
chrome.runtime.onStartup.addListener(async () => {
console.log('🍄 监听启动事件', Date.now());
});
content
触发接收事件
// 接收popup事件、触发事件
chrome.runtime.onMessage.addListener((msg) => {
console.log('🍄 接收popup', msg);
chrome.runtime.sendMessage(msg, (response) => {
console.log('🍄 触发事件', Date.now(), response);
});
})
// 建立长链接、发送消息、接收消息
const port = chrome.runtime.connect({ name: "from-content" });
port.postMessage({ from: "content 0" });
port.onMessage.addListener((msg) => {
console.log('🍄 ', port, msg);
if (msg.from === "background") {
port.postMessage({ from: "content", data: 'content 1' });
} else if (msg.from === "popup") {
port.postMessage({ from: "content", data: 'content 2' });
}
});
popup
触发接收事件
// 接收content事件
chrome.runtime.onMessage.addListener((msg) => {
console.log('🍄 接收content', msg);
});
// 触发事件
chrome.runtime.sendMessage({ type: 'geo' });
// 建立长链接、发送消息、接收消息
const port = chrome.runtime.connect({ name: "from-popup" });
port.postMessage({ from: "popup" });
port.onMessage.addListener(function (msg) {
if (msg.from === "background") {
port.postMessage({ from: "popup", data: 'popup 0' });
} else if (msg.from === "content") {
port.postMessage({ from: "popup", data: 'popup 1' });
}
});
2. 插件与插件之间的通讯
插件A 发送消息
// 需要发送到的插件的id
const receivedExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// 单次消息
chrome.runtime.sendMessage(receivedExtensionId, { from: "a" }, (response) => {
console.log('🍄 ', response);
});
// 长连接
var port = chrome.runtime.connect(receivedExtensionId);
port.postMessage({ from: "a" });
插件B 接收消息
// 接收消息
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
if (request.from === "a") {
sendResponse({targetData: "b"});
}
}
);
// 接收长连接
chrome.runtime.onConnectExternal.addListener((port) => {
port.onMessage.addListener((msg) => {
if (msg.from === "a") {
port.postMessage({targetData: "b"});
}
});
});
3. 插件与页面之间的通讯
关于插件ID
- 插件ID获取:
chrome://extensions/
-> 查看插件ID- 插件中通过
chrome.runtime.id
获取
- 插件ID若未发布到
Chrome Web Store
,使用开发者模式加载插件时,插件ID会变化。故: 未发布的插件,不要使用 External 方式
通过External
权限配置
{
"externally_connectable": {
"matches": [
"localhost:*/*",
"*://localhost/*",
"*://*.iyb.com/*",
],
},
}
页面 发送消息
// 需要发送到的插件的id
const receivedExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// 单次消息
chrome.runtime.sendMessage(receivedExtensionId, { from: "a" }, (response) => {
console.log('🍄 ', response);
});
// 长连接
var port = chrome.runtime.connect(receivedExtensionId);
port.postMessage({ from: "a" });
插件 接收消息
// 接收消息
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
if (request.from === "a") {
sendResponse({targetData: "b"});
}
}
);
// 接收长连接
chrome.runtime.onConnectExternal.addListener((port) => {
port.onMessage.addListener((msg) => {
if (msg.from === "a") {
port.postMessage({targetData: "b"});
}
});
});
通过window.postMessage
页面 发送消息
window.postMessage({
source: "webPage",
type: "message",
data: "hello"
}, "*");
插件content 接收消息
只有在插件
content
中才能接收到window
消息。注:
background
中无法接收window
- 页面中的
window.postMessage
必须在content
加载完成之后才能发送
window.addEventListener("message", function (event) {
if (event.source !== window) return;
if (event.data.source === "webPage") {
console.log('🍄 ', event.data);
// 向popup发送消息
chrome.runtime.sendMessage({ from: "content", data: 'content 0' });
}
}, false);