構建跨瀏覽器擴充套件程式

瀏覽器擴充套件 API 的引入為瀏覽器擴充套件的開發建立了一個統一的平臺。然而,使用擴充套件 API 的瀏覽器(主要是 Chrome、Edge、Firefox、Opera 和 Safari)在 API 實現和覆蓋範圍上存在差異。

要最大限度地擴大瀏覽器擴充套件的覆蓋範圍,意味著至少要為兩種瀏覽器開發,甚至可能更多。本文著眼於建立跨瀏覽器擴充套件時面臨的主要挑戰,並提出了應對這些挑戰的方法。

注意:主要瀏覽器已採用 Manifest V3。此 Manifest 版本在瀏覽器擴充套件環境(例如用於處理非同步事件的 Promise)之間提供了更好的相容性。除了本指南中的資訊外,請參閱 FirefoxChrome 的 Manifest V3 遷移指南。

跨平臺擴充套件編碼障礙

在處理跨平臺擴充套件時,您需要解決以下幾個方面的問題:

API 名稱空間

主要瀏覽器中使用了兩種 API 名稱空間:

  • browser.*,Firefox 和 Safari 使用的擴充套件 API 的擬議標準。
  • chrome.*,Chrome、Opera 和 Edge 使用。

Firefox 還支援 chrome.* 名稱空間,用於與 Chrome 相容的 API,主要用於協助移植。然而,首選使用 browser.* 名稱空間。除了是擬議標準之外,browser.* 還使用 Promise——一種現代且方便的非同步事件處理機制。

只有在最微不足道的擴充套件中,名稱空間才可能是唯一需要解決的跨平臺問題。因此,單獨解決這個問題很少有用。最好的方法是結合非同步事件處理來解決這個問題。

API 非同步事件處理

隨著 Manifest V3 的引入,所有主要瀏覽器都採用了從非同步方法返回 *Promise* 的標準。Firefox 和 Safari 對所有非同步 API 都完全支援 Promise。從 Chrome 121 開始,除非另有說明,所有非同步擴充套件 API 都支援 Promise。`devtools` API 是唯一沒有 Promise 支援的 API 名稱空間 (Chromium bug 1510416)。

在 Manifest V2 中,Firefox 和 Safari 支援非同步方法的 Promise。同時,Chrome 方法呼叫 *回撥*。為了相容性,所有主要瀏覽器在所有 Manifest 版本中都支援回撥。詳情請參見 回撥和 Promise

某些擴充套件 API 事件的處理程式需要透過 Promise 或回撥函式進行非同步響應。例如,runtime.onMessage 事件的處理程式可以使用 Promise 傳送非同步響應或使用回撥。事件處理程式返回的 Promise 在 Firefox 和 Safari 中受支援,但在 Chrome 中尚未受支援。

Firefox 還支援對支援 chrome.* 名稱空間的 API 進行回撥。然而,建議使用 Promise。Promise 極大地簡化了非同步事件處理,尤其是在您需要將事件鏈式連線在一起時。這意味著使用 polyfill 或類似工具,以便您的擴充套件在 Firefox 和 Safari 中使用 browser.* 名稱空間,在 Chrome、Opera 和 Edge 中使用 chrome.*

注意:如果您不熟悉這兩種方法的區別,請參閱 瞭解非同步 JavaScript:回撥、Promise 和 Async/Await 或 MDN 使用 Promise 頁面。

WebExtension 瀏覽器 API Polyfill

那麼,如何輕鬆利用 Promise 呢?解決方案是使用 Promise 為 Firefox 編寫程式碼,並使用 WebExtension 瀏覽器 API Polyfill 來支援 Chrome、Opera 和 Edge。

此 polyfill 解決了 Firefox、Chrome、Opera 和 Edge 之間的 API 名稱空間和非同步事件處理問題。

要使用 polyfill,請使用 npm 將其安裝到您的開發環境中,或直接從 GitHub releases 下載。

然後,在以下位置引用 browser-polyfill.js

  • manifest.json,使其可用於後臺指令碼和內容指令碼。
  • HTML 文件,例如 browserAction 彈出視窗或選項卡頁面。
  • tabs.executeScript 動態注入的內容指令碼中的 executeScript 呼叫,如果它尚未透過 manifest.json 中的 content_scripts 宣告載入。

例如,此 manifest.json 程式碼使 polyfill 可用於後臺指令碼。

json
{
  // …
  "background": {
    "scripts": ["browser-polyfill.js", "background.js"]
  }
}

您的目標是確保 polyfill 在您的擴充套件中執行,然後執行任何其他需要 browser.* API 名稱空間的指令碼。

注意:有關使用 polyfill 與模組打包器的更多詳細資訊和資訊,請參閱專案在 GitHub 上的自述檔案。

還有其他 polyfill 選項。然而,在撰寫本文時,其他選項都沒有提供 WebExtension 瀏覽器 API Polyfill 的覆蓋範圍。因此,如果您沒有將 Firefox 作為首選目標,您的選擇是接受替代 polyfill 的限制,移植到 Firefox 並新增跨瀏覽器支援,或者開發自己的 polyfill。

API 函式覆蓋

主要瀏覽器中提供的 API 函式差異分為三大類:

  1. 缺乏對整個函式的支援。例如,在撰寫本文時,Edge 不支援 browserSettings 函式。
  2. 函式內功能支援的差異。例如,在撰寫本文時,Firefox 不支援通知函式方法 notifications.onButtonClicked,而 Firefox 是唯一支援 notifications.onShown 的瀏覽器。
  3. 專有功能,支援特定於瀏覽器的功能。例如,在撰寫本文時,容器是 Firefox 特有的功能,由 contextualIdentities 函式支援。

有關主要瀏覽器、Firefox for Android 和 Safari on iOS 對擴充套件 API 支援的詳細資訊,可以在 Mozilla Developer Network 的JavaScript API 瀏覽器支援頁面上找到。瀏覽器相容性資訊也包含在 Mozilla Developer Network JavaScript API 參考頁面的每個函式及其方法、型別和事件中。

處理 API 差異

解決 API 差異的一種簡單方法是將您的擴充套件中使用的函式限制為在所有目標瀏覽器中提供相同功能的函式。實際上,這種方法對於大多數擴充套件來說可能過於嚴格。

相反,當 API 之間存在差異時,您應該提供替代實現或回退功能。(請記住:您可能還需要這樣做,以考慮*相同*瀏覽器不同版本之間 API 支援的差異。)

使用執行時檢查函式功能的可用性是實現替代或回退功能的推薦方法。執行執行時檢查的好處是,當函式可用時,您無需更新和重新分發擴充套件即可利用該函式。

以下程式碼使您能夠執行執行時檢查:

js
if (typeof fn === "function") {
  // safe to use the function
}

內容指令碼執行上下文

內容指令碼可以訪問和修改頁面的 DOM,就像頁面指令碼一樣。它們還可以檢視頁面指令碼對 DOM 所做的任何更改。然而,內容指令碼獲得的是 DOM 的“乾淨”檢視。

Firefox 和 Chrome 使用根本不同的方法來處理此行為:在 Firefox 中,它被稱為 Xray 視覺,而 Chrome 使用隔離世界。有關更多詳細資訊,請參閱內容指令碼概念文章的內容指令碼環境部分。

然而,Firefox 提供了一些 API,使內容指令碼能夠訪問由頁面指令碼建立的 JavaScript 物件,並將其 JavaScript 物件暴露給頁面指令碼。有關詳細資訊,請參閱與頁面指令碼共享物件

內容指令碼的內容安全策略 (CSP) 也存在差異。

後臺頁面和擴充套件服務工作執行緒

作為 Manifest V3 實現的一部分,Chrome 用擴充套件服務工作執行緒取代了後臺頁面。Firefox 保留了後臺頁面的使用,而 Safari 支援後臺頁面和服務工作執行緒。

有關更多資訊,請參閱 "background" Manifest 鍵頁面上的瀏覽器支援部分。其中包括如何實現跨瀏覽器指令碼的示例。

Manifest 鍵

主要瀏覽器支援的 manifest.json 檔案鍵的差異大致分為三類:

  1. 擴充套件資訊屬性。例如,在撰寫本文時,Firefox 和 Opera 包含 developer 鍵,用於提供擴充套件開發者的詳細資訊。
  2. 擴充套件功能。例如,在撰寫本文時,Chrome 不支援 browser_specific_settings 鍵。
  3. 鍵的可選性。在撰寫本文時,通常只有 "manifest_version""version""name" 是強制鍵。

瀏覽器相容性資訊包含在 Mozilla Developer Network manifest.json 鍵參考頁面的每個鍵中。

由於 manifest.json 檔案很少更改——除了釋出版本號,這可能在不同瀏覽器之間有所不同——為每個瀏覽器建立和編輯一個靜態版本通常是最簡單的方法。

擴充套件打包

為透過瀏覽器擴充套件商店分發而打包擴充套件程式相對簡單。Firefox、Chrome、Edge 和 Opera 都使用簡單的 zip 格式,要求 manifest.json 檔案位於 zip 包的根目錄。Safari 要求擴充套件程式以類似於應用程式的方式打包。

有關打包的詳細資訊,請參閱各個擴充套件程式開發者門戶上的指南。

擴充套件釋出

各大瀏覽器都維護著瀏覽器擴充套件商店。每個商店還會審查您的擴充套件程式,以檢查是否存在安全漏洞。

因此,您需要單獨處理每個商店的擴充套件程式新增和更新。

下表總結了每個商店的方法和功能

瀏覽器 註冊費 上傳工具 釋出前稽核流程 賬戶雙重身份驗證

Chrome

自動,不到一小時

Edge

未提供 SLA

Firefox

web-ext

自動,幾秒鐘。

擴充套件釋出後會進行手動審查,如果發現需要修復的問題,可能會導致擴充套件暫停。

Opera

手動,未提供 SLA

Safari

有,根據 Apple 的說法,平均 50% 的應用程式在 24 小時內完成審查,超過 90% 的應用程式在 48 小時內完成審查。

其他注意事項

版本號

Firefox、Chrome 和 Edge 商店要求每個上傳的版本都有不同的版本號。這意味著如果您在釋出版本中遇到問題,您不能回滾到早期版本號。

總結

在進行跨平臺擴充套件開發時,可以透過將目標設定為 Firefox 並使用 WebExtension 瀏覽器 API Polyfill 來解決擴充套件 API 實現之間的差異。

您的大部分跨平臺工作可能會集中在處理主要瀏覽器支援的 API 功能之間的差異。您可能還需要考慮內容指令碼和後臺指令碼實現之間的差異。建立 manifest.json 檔案應該相對簡單,並且您可以手動完成。然後,您需要考慮提交到每個擴充套件商店的過程中的差異。

遵循本文中的建議,您應該能夠建立一個在所有四個主要瀏覽器上都能良好執行的擴充套件,從而使您的擴充套件功能能夠惠及更多人。