> ## 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 plugin。如果你已不再侷限於只支援 Chrome、想要一流的 Firefox 輸出,或被重新載入循環的小毛病困擾,本指南會在不重寫 UI 程式碼的前提下,把典型的 CRXJS 專案遷移到 Extension.js。

<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 dev server,內建瀏覽器啟動、設定檔管理與各目標的重新載入。

## 步驟 1:安裝 Extension.js

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

如果你的專案還會把 Vite 用在非擴充功能的範圍(例如行銷網站),請把 Vite 安裝侷限在那個工作區。

## 步驟 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,請直接刪除。如果它還有其他 plugin,請把對應功能移到 [`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 script:定向重新載入。
* 背景 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}]` 區塊。兩個框架都會輸出正確結構;直接照搬現有項目即可。
* **帶 hash 的資產路徑:** Extension.js 在 [`dist/<browser>`](/docs/features/path-resolution) 下使用穩定路徑。如果你的程式碼硬寫了 Vite 風格的 hash 檔名,請改用 manifest 相對路徑。

## 延伸閱讀

* [跨瀏覽器相容性](/docs/features/cross-browser-compatibility)
* [瀏覽器專屬的 manifest 欄位](/docs/features/browser-specific-fields)
* [重新載入與 HMR](/docs/features/reload-and-hmr)
* [Manifest V3 疑難排解](/docs/concepts/manifest-v3)
