> ## Documentation Index
> Fetch the complete documentation index at: https://extension.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# 从 CRXJS 迁移到 Extension.js

> 把 Vite + CRXJS 的浏览器扩展工程分步迁移到 Extension.js。干净地映射 manifest、入口点、脚本和 HMR 行为。

[CRXJS](https://crxjs.dev) 是用于 Chrome 扩展的 Vite 插件。如果你已经超出了只面向 Chrome 的范畴，希望获得一等的 Firefox 输出，或者遇到了重载循环的怪问题，本指南可以把一个典型的 CRXJS 项目迁移到 Extension.js，而无需重写 UI 代码。

<Note>
  你来这里是因为在 Vite 8 上 `vite build` 报错 `[crx:manifest-post] Content
      script fileName is undefined`？在迁移前请先查看 [专门的修复
  页面](/docs/migrate/crxjs-content-script-filename-undefined) 了解变通方案。
</Note>

## 哪些变，哪些不变

**不变的：** 你的 React/Vue/Svelte 组件、你的 Tailwind 配置、你的测试、你的 `chrome.*` API 调用。

**会变的：**

* `vite.config.ts` + `@crxjs/vite-plugin` 变成 `extension.config.js`（或干脆不要配置）。
* `manifest.config.ts`（TypeScript 模块）变成普通的 `manifest.json`，可配合 [按浏览器前缀的键](/docs/features/browser-specific-fields)。
* `vite` / `vite build` 脚本变成 `extension dev` / `extension build`。
* Extension.js 的 dev 取代 Vite 开发服务器，内置浏览器启动、配置文件管理和按目标的重载。

## 第 1 步：安装 Extension.js

```bash theme={null}
npm install extension@latest --save-dev
npm uninstall @crxjs/vite-plugin vite
```

如果你的项目也在非扩展场景（例如一个营销站点）中使用 Vite，请把 Vite 保留在那个 workspace 范围内。

## 第 2 步：转换 manifest

CRXJS 使用一个 TypeScript 模块：

```ts manifest.config.ts theme={null}
import { defineManifest } from "@crxjs/vite-plugin";
import pkg from "./package.json";

export default defineManifest({
  manifest_version: 3,
  name: pkg.name,
  version: pkg.version,
  action: { default_popup: "src/popup/index.html" },
  background: { service_worker: "src/background/index.ts" },
  content_scripts: [{ matches: ["<all_urls>"], js: ["src/content/index.ts"] }],
});
```

在 Extension.js 中，写一个真实的 `manifest.json` 并引用真实的文件：

```json manifest.json theme={null}
{
  "manifest_version": 3,
  "name": "my-extension",
  "version": "1.0.0",
  "action": { "default_popup": "popup/index.html" },
  "background": { "service_worker": "background/index.ts" },
  "content_scripts": [{ "matches": ["<all_urls>"], "js": ["content/index.ts"] }]
}
```

注意：

* 在 manifest 中文件扩展名仍然是 `.ts`/`.tsx`。Extension.js 会在构建时编译它们。
* 如果你扁平化了目录，去掉 `src/` 前缀。Extension.js 会遵循你的 manifest 声明的路径。
* 对于按浏览器的取值，请使用 [带前缀的键](/docs/features/browser-specific-fields)（`firefox:browser_specific_settings`），而不是按构建维护多份 manifest。

## 第 3 步：更新 package.json 脚本

把：

```json theme={null}
{
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  }
}
```

替换为：

```json theme={null}
{
  "scripts": {
    "dev": "extension dev",
    "build": "extension build",
    "start": "extension start"
  }
}
```

完整命令集见 [命令参考](/docs/commands/index)。

## 第 4 步：删除 vite.config.ts

如果你的 `vite.config.ts` 只是为接 CRXJS 而存在，直接删掉。如果里面还有其他插件，把等价配置迁移到 [`extension.config.js`](/docs/features/extension-configuration)，或者用 [Rspack 配置](/docs/features/rspack-configuration) 做更高级的打包器定制。

## 第 5 步：处理 HMR 差异

CRXJS 的 HMR 通过 Vite 的 WebSocket 推送更新。Extension.js 使用一种不同的模型，文档见 [重载与 HMR](/docs/features/reload-and-hmr)：

* popup、options、devtools 页面：HMR。
* Content scripts：精准重载。
* 背景 service worker：完整重启。

如果你的代码依赖 Vite 的 `import.meta.hot` 来处理 content script 逻辑，请把那些分支替换为普通的模块代码。Extension.js 在你的源代码之外编排重载。

## 第 6 步：跨浏览器输出

CRXJS 面向 Chromium。要同时开始输出 Firefox：

```bash theme={null}
extension build --browser=chrome,firefox --zip
```

你会得到 `dist/chrome` 和 `dist/firefox`，含按浏览器正确的 manifest，以及可直接上架 Chrome Web Store 和 addons.mozilla.org 的 `.zip` 归档。参见 [跨浏览器兼容性](/docs/features/cross-browser-compatibility)。

## 第 7 步：验证

```bash theme={null}
extension dev --browser=chrome
```

验证 popup、options、content script 和背景行为。然后验证 Firefox：

```bash theme={null}
extension dev --browser=firefox
```

如果你的代码调用 `chrome.*` 而你希望同一份源在 Firefox 上无需改动就能运行，请使用 `--polyfill`。

## 常见坑点

* **Service worker 的 `import` 语句：** 当你的背景入口使用 ESM 语法时，Extension.js 会自动设置 `type: "module"`。CRXJS 也是这么做的。无需额外处理。
* **`web_accessible_resources` 的类型：** Manifest V3 使用 `[{resources, matches}]` 块。两个框架都会输出正确的形状；按原样复制现有条目即可。
* **带哈希的资源路径：** Extension.js 在 [`dist/<browser>`](/docs/features/path-resolution) 下使用稳定的路径。如果你的代码硬编码了 Vite 风格的带哈希文件名，请替换为相对于 manifest 的路径。

## 参见

* [跨浏览器兼容性](/docs/features/cross-browser-compatibility)
* [按浏览器的 manifest 字段](/docs/features/browser-specific-fields)
* [重载与 HMR](/docs/features/reload-and-hmr)
* [Manifest V3 排错](/docs/concepts/manifest-v3)
