Chrome扩展Manifest V3消息传递丢失Map对象问题排查
问题
开发Chrome扩展时,通过content script抓取数据并将包含字符串、整数、数组及3个Map对象的复合对象传递给background worker,传递过程中Map对象被剥离——发送前和接收后打印对象,发现原本的Map属性变为空对象。相关代码示例如下:
内容脚本代码
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.attemptedStartScrapping) { try { fetchInfo() .then(info => { console.log({content: info}) sendResponse(info) }) return true } catch (e) { if(!(e instanceof NotHomepageException)) console.error(e) } } })
后台工作线程代码
chrome.action.onClicked.addListener(tab => { const details = { tabId: tab.id } sendMessage(details) }) function sendMessage(details) { chrome.tabs.sendMessage( details.tabId, { attemptedStartScrapping: true, tabId: details.tabId ?? null, }, info => { console.log({ worker: info }) } ) }
控制台输出说明
- 内容脚本控制台:打印的
info对象包含完整的Map属性,数据正常 - 后台控制台:接收到的
info对象中,原Map属性变为空对象
这是消息传递的bug吗?
解决方案
这不是Chrome扩展消息传递的bug,而是消息传递的序列化机制限制导致的。Chrome扩展的runtime.sendMessage/tabs.sendMessage使用JSON序列化传递数据,而原生JSON不支持Map、Set、Date等复杂类型的序列化——当序列化Map时,会被转换为空对象{},因此接收端无法拿到原Map数据。
解决方法是在发送前将Map转换为JSON可序列化的格式,接收后再还原为Map:
步骤1:发送端(content script)转换Map
修改fetchInfo的处理逻辑,将对象中的Map转换为数组或普通对象:
fetchInfo().then(info => { // 将Map转换为可序列化的数组格式 const serializableInfo = { ...info, map1: Array.from(info.map1), map2: Array.from(info.map2), map3: Array.from(info.map3) // 也可转为普通对象:Object.fromEntries(info.map1) } console.log({content: serializableInfo}) sendResponse(serializableInfo) })
步骤2:接收端(background worker)还原Map
在接收回调中将数组或普通对象还原为Map:
chrome.tabs.sendMessage( details.tabId, { attemptedStartScrapping: true, tabId: details.tabId ?? null }, info => { // 还原Map结构 const restoredInfo = { ...info, map1: new Map(info.map1), map2: new Map(info.map2), map3: new Map(info.map3) // 若之前转成普通对象,用new Map(Object.entries(info.map1))还原 } console.log({ worker: restoredInfo }) } )
注意事项
- 除Map外,Set、Date、RegExp等复杂类型也会遇到类似序列化问题,需按相同逻辑处理
- 若对象嵌套较深,建议编写递归转换函数,自动处理所有嵌套的复杂类型
内容的提问来源于stack exchange,提问作者César Rodriguez
相关产品推荐
相关产品推荐

