跳轉到主要內容
Manifest V3(MV3)把背景頁面換成 service worker、強化內容安全政策,並重塑了擴充功能宣告網路與主機存取的方式。多數日常痛點其實都源自同一個原因:MV3 假設背景程式碼是暫時性、由事件驅動的。Chrome 與 Firefox 在少數鍵上也有不同解讀。 本頁彙整建置 MV3 擴充功能時最常出現的問題,附上明確修法以及 Extension.js 為你處理的部分。

Manifest V3 背景的 service_worker 搭配 type: "module"

Chromium 在 MV3 的背景採用 service worker。若要從中 import ES module,manifest 需設定 type: "module":
{
  "manifest_version": 3,
  "background": {
    "service_worker": "service_worker.js",
    "type": "module"
  }
}
少了 "type": "module",import 陳述句會在註冊時失敗,而且擴充功能主控台不會出現明確錯誤。加上之後,你就能在 worker 檔案中撰寫現代 ES module 語法。 當你的背景進入點使用 ES module 語法時,Extension.js 會自動設定 type: "module"。它也會把 .ts/.tsx worker 編譯成單一打包輸出,讓 module 解析在正式環境中仍能正常運作。

為什麼 background.js 在 Manifest V3 行為不同

在 Manifest V2 中,background.js 在常駐的背景頁面執行,具有 DOM。在 Chromium 上的 Manifest V3,背景則以 service worker 執行:
  • 沒有 DOM。windowdocumentXMLHttpRequestlocalStorage 都不存在。請改用 fetchchrome.storage
  • worker 隨時可能在閒置時被終止,並在下一次事件喚醒。不要將狀態存在 module 範圍變數中;請持久化到 chrome.storage
  • 允許頂層 await,但長時間初始化本身無法讓 worker 維持存活。
  • 在檔案最上方同步註冊事件監聽器,不要放在 async callback 內。註冊在 async callback 內的監聽器,不會在喚醒 worker 的那次事件中觸發。
Firefox 採用非持久化的 event page,而不是 service worker。Extension.js 會把 Chromium 路由到 service_worker、Firefox 路由到 scripts,共用同一份原始碼,使同一個背景進入點能依目標正確編譯。

Manifest V3 的 web_accessible_resources

Manifest V3 把 web_accessible_resources 從扁平字串陣列改為 { resources, matches } 區塊清單:
{
  "web_accessible_resources": [
    {
      "resources": ["images/logo.png", "pages/injected.html"],
      "matches": ["https://example.com/*"]
    }
  ]
}
常見錯誤:
  • 列出不在建置輸出裡的路徑。Extension.js 只會輸出被 manifest.json、進入點 HTML 或被 import 程式碼引用的檔案。請把該檔案加為資產,讓它落到 dist/<browser>
  • 忘了 matches。少了它,資源不會暴露給任何來源。
  • 使用 V2 風格的扁平字串。瀏覽器在 MV3 會安靜忽略這些。
完整範例(包含把擴充功能 URL 注入 content script)請見 web_accessible_resources 實作

host_permissionspermissions

Manifest V3 把主機存取從 permissions 陣列拆出來:
控管的內容
permissions具名 API 介面(storagetabscookiesscriptingalarms 等)。
host_permissions擴充功能可以讀取或修改的 URL match 樣式(https://*/**://api.example.com/*)。
optional_permissionsoptional_host_permissions在執行期透過 chrome.permissions.request 請求的權限。
把它們搞混的徵兆:
  • 對你擁有存取權的網站呼叫 chrome.cookies.get 回傳空結果:該 URL 需要 host_permissions,光有 cookies 不夠。
  • chrome.scripting.executeScript 出現 “Cannot access contents of url”:請把該 URL 加入 host_permissions
  • 你只需要一個來源時,Web Store 卻警告使用者擁有「全站」存取權:請收斂主機樣式。
各 API 的對照請見權限與主機權限

Firefox 的 declarative_net_request

declarative_net_request(DNR)是 MV3 用來取代封鎖型 webRequest 的方案。Firefox 支援它,但有幾項限制:
  • 靜態規則資源檔(rule_resources)必須是有效的 JSON 陣列。Firefox 對於空或格式錯誤的規則檔比 Chrome 更嚴格。
  • Chrome 支援的部分規則動作與條件在 Firefox 仍是部分支援。最新的對應表請參閱 MDN 的 declarativeNetRequest 文件
  • 跨來源請求要套用 DNR 規則,必須有 host_permissions(或 <all_urls>)。
Extension.js 會在建置期驗證 DNR 資源,並輸出每個瀏覽器專屬的成品,讓 Firefox 專屬的規則檔不會跑到 Chrome 的建置中。

Content script、worker 與擴充功能 URL

三個 MV3 容易讓人踩雷的地方:
  • Content script 無法存取頁面的 JavaScript scope。 它們共享 DOM,但不共享 window。請用 window.postMessage(或指向 web_accessible_resources 項目的 <script> 標籤)與頁面程式碼溝通。
  • service worker 在安裝過程中無法可靠呼叫 chrome.runtime.getURL 請在需要 URL 的事件處理函式內延後解析。
  • 擴充功能 URL 以 origin 為界。 你擴充功能中的兩個頁面之間可以互相對話,但網頁無法從外部抓取它們,除非它們列在 web_accessible_resources 中。

Extension.js 如何協助處理 Manifest V3

  • 自動把背景進入點路由到 service_worker(Chromium)或 scripts(Firefox)。
  • 當背景使用 ES module 語法時,自動設定 type: "module"
  • 在建置期針對作用中的 manifest 版本驗證 web_accessible_resourcespermissionshost_permissions
  • 輸出每個瀏覽器專屬的 dist/<browser> 成品,讓 Chrome 與 Firefox 之間的 MV3 差異彼此隔離。
  • extension dev 期間,儲存時重新載入 content script、service worker 與 HTML 頁面。

下一步