插件编译入口
插件的入口很多,比如加载到页面中的 content、点击插件弹出的 popup、侧边栏 sidepanel
我看录音和截屏这里的文档,想着确实有的功能模块比如录音录像,用一个全新的标签页或者 window 去打开会比较好。
结果如果你新建一个 HTML 发现 manifest 里面没有引用,vite 里面也没引用,根本就不会编译了,又不可能直接在 html 里面写原生,感觉不是很优雅。还是写 react 然后挂载到这个 html 里面吧,虽然可能直接原生 js 更方便做小 demo。
就需要在 vite 里面配置一下入口,相关的文件就会编译了。
rollupOptions: {
input: {
popup: "src/popup/index.html",
sidepanel: "src/sidepanel/index.html",
record: "src/content/record/index.html",
},
},
```插件 shadowdom
插件由于需要在不同的页面上加载,难免会遇到样式冲突问题,shadowdom 看上去很美好,样式隔离、不用额外处理比如添加前缀之类的。
但是实际还是有问题,比如原页面:root 的样式比如变量,还是会继承下来。
比如 react 实际上不会把 tsx 引入的 css 文件加载到 shadowdom 下面,而是直接加载到全局,然后就被隔离了。
最后只能是折中中的折中,引入 css 文件然后在创建 shadowdom 的时候加一个 style 标签进去,除了以后的其他组件样式都要手动添加一遍,倒也不算特别大的问题。使用 styled-component 好像也会有这个问题。
毕竟不隔离样式的话感觉更容易出问题而且不好排查。到时候如果还要反过来修改原网页的样式就会比较麻烦。人家更新一下这边就全乱完了。
// 把 content 下的 css 以原始文本方式导入并注入到 shadowRoot 中,
// 否则 Vite 会把样式注入到 document.head,shadow DOM 内的元素看不到这些样式。
// 使用 ?raw 从 Vite 获取原始文本内容。
// @ts-ignore
import appCss from "./App.css?raw";
// @ts-ignore
import globalCss from "../../styles/global.css?raw";
// * react会把css放入document中,而不是shadowRoot中,需要在顶层script中手动注入css样式
// 将样式注入到 shadowRoot 中(把 global.css 放到最前面)
const style = document.createElement("style");
style.textContent = [globalCss, appCss].join("\n\n");
shadowRoot.appendChild(style);background 消息返回 sendResponse 延迟问题
之前写其他项目的时候就遇到过,明明文档里写的是 return true 就可以了,结果还是不行,返回的内容总是 undefined。后来想办法用插件的 storage 来存储返回值,然后在需要的地方监听取出来。
结果终于找到问题了,onMessage 监听函数不能当作一个异步函数来处理。不能先 await sendResponse() 再 return true。但是不异步的话,return true 之后函数就运行完了。
这里就需要 settimeout 了,setTimeout(async () => {}),settimeout 一个异步函数,这里面写 sendResponse() 就可以了。
case BackgroundMessageAction.RefreshUserState:
// 异步处理不能放在omessage里面,需要单独用setTimeout包裹,同时return true
setTimeout(async () => {
const res = await getUserState()
sendResponse(res)
})
return true如何在插件中状态管理
状态存在哪里都会有点问题,如果在 background 通信就会比较麻烦,在 content 或者 popup 里面就更不合适了,而且 popup 打开关闭的频率很高,导致 popup 里面拿到的数据也是经常出问题。
考虑之后还是放在 background 呗,数据通过 zustand/vanilla 存储,port 断开时取消订阅
export const TaskControlStorePortName = "taskControlStorePortName";
chrome.runtime.onConnect.addListener((port) => {
if (port.name === TaskControlStorePortName) {
const unsub = taskControlStore.subscribe((state) => {
postMessage(state.taskQueue);
});
port.onDisconnect.addListener(() => {
unsub();
});
}
});