Chrome扩展:如何从background.js向popup.js传递变量?
嘿,我完全懂你现在的困扰——Chrome扩展的消息传递是异步的,而React的初始渲染是同步执行的,导致你还没拿到startingTime就已经把undefined传给了Timer组件。另外直接访问背景页变量没成功,是因为你用Babel打包后,background.js里的startTime是模块作用域的,并没有暴露到背景页的全局window对象上。咱们来看看几个靠谱的解决办法:
方案1:在消息回调中初始化React组件
最直接的方式就是等异步消息返回后再执行render,确保拿到数据后再渲染组件:
修改你的popup.js:
import "../css/popup.css"; import Timer from "./popup/timer_component.js"; import React from "react"; import { render } from "react-dom"; const console = chrome.extension.getBackgroundPage().console; // 等待消息返回后再渲染 chrome.runtime.sendMessage({request: "getStartTime"}, function(response) { const startingTime = response.done; render( <Timer currentTimer={startingTime}/>, window.document.getElementById("app-container") ); });
这样就能保证startingTime有值的时候才渲染Timer组件,不会出现undefined的问题。
方案2:用React状态管理实现动态更新(更推荐)
如果之后你需要实时更新计时器数据,或者希望组件能响应数据变化,用React的状态会更灵活。改成函数组件,用useState和useEffect来处理异步逻辑:
import "../css/popup.css"; import Timer from "./popup/timer_component.js"; import React, { useState, useEffect } from "react"; import { render } from "react-dom"; const PopupApp = () => { const [startingTime, setStartingTime] = useState(null); useEffect(() => { // 发送消息获取起始时间 chrome.runtime.sendMessage({request: "getStartTime"}, function(response) { setStartingTime(response.done); }); }, []); // 可以加个加载状态,避免显示空内容 if (startingTime === null) { return <div>加载中...</div>; } return <Timer currentTimer={startingTime}/>; }; render(<PopupApp />, window.document.getElementById("app-container"));
这个方案的好处是,未来如果需要更新startingTime(比如计时器实时刷新),只需要调用setStartingTime,组件就会自动重新渲染。
补充:关于直接访问背景页变量的问题
你之前尝试startingTime = chrome.extension.getBackgroundPage().startTime没成功,是因为Babel打包后,background.js里的let startTime是模块私有变量,不会挂载到背景页的window对象上。如果想用这种同步方式,需要把变量暴露到全局:
修改background.js:
import '../img/icon-128.png' import '../img/icon-34.png' const console = chrome.extension.getBackgroundPage().console; // 挂载到window上,让popup能访问到 window.startTime = Date.now(); chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { if (message.request == "getStartTime") { sendResponse({done: window.startTime}); } });
然后在popup.js里可以同步获取:
const backgroundPage = chrome.extension.getBackgroundPage(); const startingTime = backgroundPage.startTime;
不过这种方式不如消息传递安全,而且如果背景页的变量有更新,popup不会自动感知到,还是推荐用React状态+消息传递的方式。
内容的提问来源于stack exchange,提问作者mcody

