你来这里是因为在 Vite 8 上
vite build 报错 [crx:manifest-post] Content script fileName is undefined?在迁移前请先查看 专门的修复
页面 了解变通方案。哪些变,哪些不变
不变的: 你的 React/Vue/Svelte 组件、你的 Tailwind 配置、你的测试、你的chrome.* API 调用。
会变的:
vite.config.ts+@crxjs/vite-plugin变成extension.config.js(或干脆不要配置)。manifest.config.ts(TypeScript 模块)变成普通的manifest.json,可配合 按浏览器前缀的键。vite/vite build脚本变成extension dev/extension build。- Extension.js 的 dev 取代 Vite 开发服务器,内置浏览器启动、配置文件管理和按目标的重载。
第 1 步:安装 Extension.js
第 2 步:转换 manifest
CRXJS 使用一个 TypeScript 模块:manifest.config.ts
manifest.json 并引用真实的文件:
manifest.json
- 在 manifest 中文件扩展名仍然是
.ts/.tsx。Extension.js 会在构建时编译它们。 - 如果你扁平化了目录,去掉
src/前缀。Extension.js 会遵循你的 manifest 声明的路径。 - 对于按浏览器的取值,请使用 带前缀的键(
firefox:browser_specific_settings),而不是按构建维护多份 manifest。
第 3 步:更新 package.json 脚本
把:第 4 步:删除 vite.config.ts
如果你的vite.config.ts 只是为接 CRXJS 而存在,直接删掉。如果里面还有其他插件,把等价配置迁移到 extension.config.js,或者用 Rspack 配置 做更高级的打包器定制。
第 5 步:处理 HMR 差异
CRXJS 的 HMR 通过 Vite 的 WebSocket 推送更新。Extension.js 使用一种不同的模型,文档见 重载与 HMR:- popup、options、devtools 页面:HMR。
- Content scripts:精准重载。
- 背景 service worker:完整重启。
import.meta.hot 来处理 content script 逻辑,请把那些分支替换为普通的模块代码。Extension.js 在你的源代码之外编排重载。
第 6 步:跨浏览器输出
CRXJS 面向 Chromium。要同时开始输出 Firefox:dist/chrome 和 dist/firefox,含按浏览器正确的 manifest,以及可直接上架 Chrome Web Store 和 addons.mozilla.org 的 .zip 归档。参见 跨浏览器兼容性。
第 7 步:验证
chrome.* 而你希望同一份源在 Firefox 上无需改动就能运行,请使用 --polyfill。
常见坑点
- Service worker 的
import语句: 当你的背景入口使用 ESM 语法时,Extension.js 会自动设置type: "module"。CRXJS 也是这么做的。无需额外处理。 web_accessible_resources的类型: Manifest V3 使用[{resources, matches}]块。两个框架都会输出正确的形状;按原样复制现有条目即可。- 带哈希的资源路径: Extension.js 在
dist/<browser>下使用稳定的路径。如果你的代码硬编码了 Vite 风格的带哈希文件名,请替换为相对于 manifest 的路径。

