跳到主要内容

消息传递

这部分将从三个方面介绍消息传递:

  • 插件内部的通讯
  • 插件与插件之间的通讯
  • 插件与页面之间的通讯

同时,穿插介绍backgroundcontent的特殊性

1. 插件内部的通讯

提示

background的特殊性:

  1. 一个长期运行的后台脚本,它的生命周期与浏览器保持一致,即使没有打开任何Tab页,background也会一直运行
  2. 没有生命周期的限制,所以常用于事件监听
  3. 无法使用DOM操作,因为没有DOM环境

content的特殊性:

  1. 一个独立于页面的脚本,它的生命周期与页面保持一致,刷新页面后,content会重新加载
  2. 因为它是在页面中运行的,所以可以直接操作页面的DOM
危险

报错: Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist. 报错原因: sendMessage时,没有接收方。可能原因:

  1. 接收方没有注册onMessage/onConnect事件
  2. 接收方未加载,如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' });
}
});

触发接收事件

// 接收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
  1. 插件ID获取:
    • chrome://extensions/ -> 查看插件ID
    • 插件中通过chrome.runtime.id获取
  2. 插件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消息。注:

  1. background中无法接收window
  2. 页面中的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);