你來到這頁是因為在 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 dev server,內建瀏覽器啟動、設定檔管理與各目標的重新載入。
步驟 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,請直接刪除。如果它還有其他 plugin,請把對應功能移到 extension.config.js,或到 Rspack 設定 做進階的打包工具客製。
步驟 5:處理 HMR 差異
CRXJS 的 HMR 透過 Vite WebSocket 推送更新。Extension.js 使用重新載入與 HMR 中說明的另一套模型:- Popup、options、devtools 頁面:HMR。
- Content script:定向重新載入。
- 背景 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 上不用修改就能執行,因為 Firefox 原生支援 chrome.* 命名空間。--polyfill 處理的是相反的方向:它在 Chromium 目標上提供 browser.*。extension dev 預設會套用它,extension build 則除非你加上這個 flag,否則保持關閉。
常見地雷
- service worker 的
import陳述句: Extension.js 會打包你的背景進入點,所以原始碼中的import不需要改動 manifest。Extension.js 不會替你加上"type": "module"。如果你需要這個鍵,請自行宣告,建置會保留它。 web_accessible_resources的型別: Manifest V3 使用[{resources, matches}]區塊。兩個框架都會輸出正確結構,直接照搬現有項目即可。- 帶 hash 的資產路徑:
dist/<browser>下的正式建置使用乾淨的檔名。extension dev會為 content script 附加一段建置 hash,例如content_scripts/content-0.813e908a.js,用來繞過擴充功能的資源快取。參見 重新載入與 HMR。如果你的程式碼硬寫了 Vite 風格的 hash 檔名,請改用 manifest 相對路徑。

