消息传递
这部分将从三个方面介绍消息传递:
- 插件内部的通讯
- 插件与插件之间的通讯
- 插件与页面之间的通讯
同时,穿插介绍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);