chrome extension 系列:第七章——消息传递

上面说到了popup、content、background这几个概念,我们可以发现,这几个模块看起来都是相互独立的,但我们会有模块间通信的需求。

举个例子,我们需要在popup中配置一些页面上的功能,比如在popup中打开一个开关,就将页面上的某个元素移除掉。但popup无法访问页面的DOM,此时我们在popup中操作时,就需要去通知content,执行content中的一些逻辑对页面进行处理。此时我们就会接触到chrome extension中的消息传递相关的内容。

消息传递在extension中可以简单地分为以下两种情况:

  • 扩展程序内消息传递(如popup -> background或sidePanel -> background)

    扩展程序内消息传递通过 chrome.runtime.sendMessage 发送消息,使用 chrome.runtime.onMessage.addEventListener来监听消息。

    如果需要判断消息来源,可以通过监听消息回调函数中的 sender 属性来判断,以下是 popup -> background 的 sender 属性示例:

    // chrome.runtime.onMessage.addEventListener 中 sender 属性
    {
        "id": "dmhihmcmfmkhdncmdaojodiifmpdgdje",
        "url": "chrome-extension://dmhihmcmfmkhdncmdaojodiifmpdgdje/src/pages/popup/index.html",
        "origin": "chrome-extension://dmhihmcmfmkhdncmdaojodiifmpdgdje"
    }
    
  • 内容脚本与扩展程序间消息传递(即content -> background与background -> content)

    内容脚本往扩展程序发消息依旧是通过 chrome.runtime.sendMessage 实现,但扩展程序往内容脚本发消息会相对复杂一点。

    在发消息时,需要先找到对应的 tab。在 chrome 浏览器中,一个浏览器可能会有许多不同的 tab,而我们的内容脚本也有可能会注入到不同的 tab 中,因此我们发消息之前需要先找到发往的地址。我们可以通过 chrome.tabs.query 这个方法找到对应的 tab 后,调用 chrome.tabs.sendMessage 方法来发送消息。

    (async () => {
      const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true});
      const response = await chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
      // do something with response here, not outside the function
      console.log(response);
    })();
    

异步消息回调

有去了解过API的同学可能会发现,chrome.runtime.sendMessage 会返回一个 Promise,Promise的结果是返回的内容。而接收方是如何发送这个回复的呢?在 chrome.runtime.onMessage 的回调函数中,有一个 sendResponse 的函数,调用这个函数即可发送回复。

// content.js
const response = await chrome.runtime.sendMessage('hello'); //response: 'nice to meet you'// background.js
chrome.runtime.onMessage.addEventListener(function(request, sender, sendResponse) {
  console.log(request);
  sendResponse('nice to meet you');
})

上面说的是理想的情况,但看到返回的结果用一个 Promise 包裹,我第一时间就想到了如果 sendResponse 不是马上发送,而是过一段时间才发送,如何也能正常地获取到返回的结果呢?

// content.js
const response = await chrome.runtime.sendMessage('fetch something'); //response: undefined// background.js
chrome.runtime.onMessage.addEventListener(function(request, sender, sendResponse) {
  console.log(request);
  fetch('https://something').then((res) => res.json()).then((res) => {
    sendResponse('I got it!');
  })
})

运行了上述代码后,我们会发现 response 居然是 undefined,而不是我们预期的内容,这是为什么呢?

在查看了官方文档后,我们可以看到这一段话:

注意:如果您使用回调,则 sendResponse() 回调仅在以下情况下有效:同步使用,或事件处理脚本返回 true 以指示它将异步响应。如果没有处理程序返回 truesendResponse() 回调被垃圾回收,系统会自动调用 sendMessage() 函数的回调。

这个GPT直译说得比较糙,总结一下就是:sendResponse只在同步使用时有效,如果需要在异步响应时使用,需要在消息处理回调函数中 return true 来告诉请求方这是一个异步响应。

那我们是不是这样改就可以了呢?

// content.js
const response = await chrome.runtime.sendMessage('fetch something'); //response: 'I got it!'// background.js
chrome.runtime.onMessage.addEventListener(function(request, sender, sendResponse) {
  console.log(request);
  fetch('https://something').then((res) => res.json()).then((res) => {
    sendResponse('I got it!');
  })
  return true;
})

好消息,这样是可以的!在使用同步方法作为消息处理回调函数时,这样是可以的。但我们平时经常会使用 async/await 写法来处理异步操作,那如果我们使用 async 方法作为消息处理回调函数时,也能正常运行吗?

// content.js
const response = await chrome.runtime.sendMessage('fetch something'); //response: undefined// background.js
chrome.runtime.onMessage.addEventListener(async function(request, sender, sendResponse) {
  console.log(request);
  const fetchResponse = await fetch('https://something');
  sendResponse('I got it!');
  return true;
})

此时我们发现,response 怎么又是 undefined 了?

这里面有一个知识点,async 函数的返回值其实是一个 Promise,这个处理函数的返回值被包装在 Promise 决议的结果中,因此 chrome 并没有接收到返回的 true 值,自然也不会把这个当成一个异步消息回调。

解决办法也很简单,我们无法使用 async 函数作为消息回调处理函数,那我们可以在同步函数内部定义一个 async 函数,并直接使用 IIFE 的方式执行,此时返回的 true 值会被 chrome 接收到,sendResponse 也能正常发送并被接收。示例代码如下:

// background.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  (async () => {
    // 在这里面写异步的代码
    // ...
    const result = await getSomething();
    sendResponse(result);
  })();
​
  // 重点!返回 true 表示要异步发送响应
  return true;
});

参考资料:stackoverflow.com/questions/4…

原文链接:https://juejin.cn/post/7349087888049766435 作者:CrabSAMA

(0)
上一篇 2024年3月23日 下午4:28
下一篇 2024年3月23日 下午4:38

相关推荐

发表回复

登录后才能评论