> ## 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.

# 從 Plasmo 遷移到 Extension.js

> 逐步遷移指南:把 Plasmo 瀏覽器擴充功能專案逐步遷移到 Extension.js。將既有檔案慣例對應到 manifest.json、轉換環境變數,並保留你的 React 元件、內容腳本、背景 service worker、彈出視窗與選項頁面 UI 程式碼。

[Plasmo](https://www.plasmo.com) 是基於 Parcel 的瀏覽器擴充功能框架，採用檔案慣例的路由方式。它的發布節奏已放緩，工具鏈相對於現代打包工具也漸顯陳舊，這也是許多團隊重新評估的原因。本指南會將典型的 Plasmo 專案遷移到 Extension.js，且不需要改寫你的 UI 程式碼。

## 哪些會變、哪些不變

**維持不變：** 你的 React 元件、Tailwind 設定、測試、`chrome.*` API 呼叫，以及 `@plasmohq/storage`（如果你有用 — 它是 `chrome.storage` 的包裝，在這裡運作方式相同）。

**會改變：**

* 檔案慣例的進入點（`popup.tsx`、`contents/*.ts`）變成真正的 `manifest.json` 中明確的條目。
* `package.json` 中的 `manifest` 欄位移到 `manifest.json`。
* `plasmo dev` / `plasmo build` / `plasmo package` 變成 `extension dev` / `extension build --zip`。
* `PLASMO_PUBLIC_*` 環境變數變成 `EXTENSION_PUBLIC_*`。

## 步驟 1：安裝 Extension.js

```bash theme={null}
npm install extension@latest --save-dev
npm uninstall plasmo
```

## 步驟 2：撰寫 manifest

Plasmo 會從 `package.json` 加上檔案慣例產生 manifest。Extension.js 則把 `manifest.json` 當作事實來源。逐條轉換每個慣例：

| Plasmo 慣例                       | `manifest.json` 條目                                        |
| ------------------------------- | --------------------------------------------------------- |
| `popup.tsx` 或 `src/popup.tsx`   | `"action": {"default_popup": "popup/index.html"}`         |
| `options.tsx`                   | `"options_ui": {"page": "options/index.html"}`            |
| `newtab.tsx`                    | `"chrome_url_overrides": {"newtab": "newtab/index.html"}` |
| `background.ts`                 | `"background": {"service_worker": "background.ts"}`       |
| `contents/inline.ts` 帶 CS 設定    | `"content_scripts": [{...}]` 條目                           |
| `package.json` 中的 `manifest` 欄位 | 直接合併到 `manifest.json`                                     |

HTML 進入點是純 HTML 檔案，用來載入你的 React 元件。一個 popup 看起來像這樣：

```html popup/index.html theme={null}
<!doctype html>
<html>
  <body>
    <div id="root"></div>
    <script src="./index.tsx" type="module"></script>
  </body>
</html>
```

無論在 manifest 或 script 標籤中，副檔名都保留 `.ts`/`.tsx`。Extension.js 會在建置時編譯它們。

## 步驟 3：轉換 content scripts

Plasmo 從匯出的 `PlasmoCSConfig` 讀取 match：

```ts contents/inline.tsx theme={null}
import type { PlasmoCSConfig } from "plasmo";

export const config: PlasmoCSConfig = {
  matches: ["https://example.com/*"],
};
```

在 Extension.js 中，match 寫在 manifest 中，檔案則是一般模組：

```json manifest.json theme={null}
{
  "content_scripts": [
    {
      "matches": ["https://example.com/*"],
      "js": ["content/inline.tsx"]
    }
  ]
}
```

如果你用過 Plasmo 的 CSUI（具備 `getRootContainer` 與 anchor 的 content script UI），請改用一般的 DOM 程式碼掛載元件：建立一個容器元素，將它附加到你原本錨定的位置，然後渲染到其中。完整模式請見 [Content scripts](/docs/implementation-guide/content-scripts)，包括以 shadow DOM 隔離樣式的方式。

## 步驟 4：環境變數與訊息傳遞

* 將 `.env` 檔案與程式碼中的 `PLASMO_PUBLIC_*` 改名為 `EXTENSION_PUBLIC_*`。參見[環境變數](/docs/features/environment-variables)。
* `@plasmohq/storage` 可以原封不動繼續使用。
* `@plasmohq/messaging` 依賴 Plasmo 的 `background/messages/*` 建置慣例。請以標準的 `chrome.runtime.onMessage` 監聽器在 background script 中取代處理函式。參見[訊息傳遞](/docs/implementation-guide/messaging)。

## 步驟 5：更新 package.json 腳本

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

Plasmo 使用 `--target=firefox-mv2` 的地方，Extension.js 直接以瀏覽器作為目標：

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

你會得到 `dist/chrome` 與 `dist/firefox`，附帶各瀏覽器正確的 manifest，以及可上傳到 Chrome Web Store 與 addons.mozilla.org 的 `.zip` 壓縮檔。

## 步驟 6：驗證

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

檢查 popup、options、content scripts 與 background 的行為，再以 `--browser=firefox` 在 Firefox 上跑相同的驗證。若你的程式碼呼叫 `chrome.*` 並希望同一份程式碼可在 Firefox 上執行，請使用 `--polyfill`。

## 常見陷阱

* **`assets/` 中的圖示：** Plasmo 會從 `assets/icon.png` 自動產生各種尺寸的圖示。Extension.js 使用 manifest 中 `"icons"` 所宣告的內容；請自行匯出所需的尺寸。參見 [Icons](/docs/implementation-guide/icons)。
* **`~` import 別名：** Plasmo 將 `~` 別名指向專案根目錄。請在 `tsconfig.json` 的 paths 中設定它，或改用相對路徑 import。參見 [Path resolution](/docs/features/path-resolution)。
* **Storage hooks：** `@plasmohq/storage/hook` 的 `useStorage` 可原封不動使用；它只依賴 React 與 `chrome.storage`。

## 延伸閱讀

* [從 CRXJS 遷移](/docs/migrate/from-crxjs)
* [Extension.js vs WXT](/docs/compare/extension-js-vs-wxt)
* [跨瀏覽器相容性](/docs/features/cross-browser-compatibility)
* [重新載入與 HMR](/docs/features/reload-and-hmr)
