基本模式
每個 content 範本都採用同一種寫法:src/content/scripts.js
data-extension-root標記宿主元素,Extension.js 靠它找到你的根。all: initial !important保護宿主元素本身。shadow root 屏蔽的是它的後代,而不是宿主。少了這一行,頁面上一條div { opacity: .8 }就會讓整個元件變淡。- 回傳的那個函式 負責移除宿主元素。Extension.js 會在掛載下一個版本之前呼叫它。
Extension.js 尋找的宿主元素
Extension.js 用一個選擇器找到你的宿主:data-extension-root 屬性或 extension-root 這個 id。沒有以 class 為基礎的寫法。屬性的值可以自由決定,"true" 只是慣例,不是要求。
extension-js-devtools 這個值是保留的。內建的開發者浮層會佔用它,而選擇器會把它排除在外,這樣兩者永遠不會互相認領對方的根。
在開發期間,Extension.js 會在你的宿主元素上蓋上一些記帳用的屬性:
這些屬性會從正式建置中移除。不要針對它們撰寫選擇器。
把 CSS 送進 shadow root
shadow root 會忽略活在它外面的樣式表。下面每一種情況,都由這一條規則解釋。你在 script 中 import 的 CSS
從 content script import 樣式表時,Extension.js 會把它內嵌成data: URL,而不是輸出一個 link。請把它取回來,再把文字放進 shadow root 內部的 <style> 元素中:
src/content/scripts.js
url() 都會在建置時被改寫,因此它解析到的是擴充功能本身,而不是頁面。
你從來不插入的 CSS
當一份樣式表進了 bundle,卻沒有任何程式碼去插入它時,Extension.js 會替你把它補進你的 shadow root。它會插入一個<style data-extjs-bundle-css="true"> 元素,作為根的第一個子節點。
這份幫忙是有條件的。只要 shadow root 裡出現了你自己的、帶有文字的 <style> 元素,Extension.js 就會移除它插入的那個元素並退開。你自己的樣式表說了算。
在 manifest.json 中宣告的 CSS
列在content_scripts[].css 之下的樣式表由瀏覽器注入,注入到頁面的 document。它永遠進不了 shadow root。
用 manifest CSS 為頁面本身設定樣式。shadow root 內部的一切,請用 import 進來的 CSS。
網頁字型
shadow root 內部的@font-face 規則不會生效。字型(font face)的解析對象是 document,而不是 shadow tree。請改為把字型註冊到 document.fonts 上。完整範例請見 CSS、Sass 與 Less。
重新載入行為
Extension.js 重新載入 content script 的方式,是把整個 bundle 重新注入一次。它不會在活著的頁面裡做模組熱抽換。 每次存檔時的流程是:- Extension.js 呼叫你上一次掛載所回傳的清理函式。
- 它移除帶有這個 script 的 owner token、且來自較舊建置的宿主元素。
- 它再次執行你的 default export,藉此建出新的宿主元素。
- 它重新整理先前由它補進去的樣式表。
同一頁面上的多個進入點
一個content_scripts 區塊中的每個檔案都有自己的重新注入索引鍵。因此某個 script 的清理只會處置它自己的宿主,絕不會動到同伴的。
請給每個進入點各自的宿主元素。兩個進入點共用一個宿主,就會在清理時互相打架。
範本
Content scripts 分組中的每個範本都掛載到 shadow root:content、content-css-modules、content-custom-font、content-env、content-less、content-less-modules、content-main-world、content-multi-one-entry、content-multi-three-entries、content-preact、content-react、content-sass、content-sass-modules、content-svelte、content-typescript、content-vue
產生 React 那個範本,看看 shadow root 裡面的框架根長什麼樣:
src/content/scripts.jsx
最佳實務
- 永遠回傳一個會移除宿主元素的清理函式。
- 宿主元素上的
all: initial !important要一直留著。 - 除非你有理由隱藏這棵樹,否則請選
mode: "open"。closed 的根比較難除錯。 - 查詢你自己的節點時,請在
shadowRoot內部查,絕不要在document裡查。 - 不要依賴
data-extjs-*屬性。它們只為開發循環而存在。
後續步驟
- 閱讀完整的 content script 撰寫合約。
- 在 CSS、Sass 與 Less 中了解樣式是怎麼路由的。
- 回顧重新載入與 HMR 的行為。

