網站導覽

指令

原生 CSS 修剪

控制每份樣式表中的原生 class 修剪與隨需產生的關鍵影格。

本頁目錄

原生 CSS 修剪會移除未使用的 class 選擇器分支,並讓關鍵影格按需可用。這是樣式表政策,與 utility class 生成分開。最佳化 CSS 交付說明何時啟用;掃描指令定義來源與候選篩選。

修剪指令止於檔案邊界

這次編譯只使用 local。app.css 啟用,cards.css 未啟用。匯入 cards.css 不會將修剪指令傳入其中。

文字說明
  1. app.css 包含 @prune native、匯入 cards.css,並定義 .local 與 .unused-local。唯一使用的 class 是 local。
  2. 其輸出保留 .local,移除 .unused-local。
  3. cards.css 未宣告修剪或保留指令。即使兩個 class 都未使用,預設政策仍保留 .card 與 .unused-card。
  4. 全專案 pruneNativeCSS 選項可另外讓專案檔案參與修剪。@preserve native 在其所屬檔案內優先。此範例單獨呈現明確的逐檔政策。

原生 CSS 預設會保留,即使提供 class 清單也一樣。在整合或樣式表 API 設定 pruneNativeCSS: true,可修剪專案擁有的來源。套件 CSS 仍保留,除非其來源明確啟用修剪。

@prune native

@prune native;.used {  color: red;}.unused {  color: blue;}

此指令只對宣告它的檔案啟用修剪,不會傳遞到匯入樣式表。同檔案中的 @preserve native; 優先,即使整合啟用全專案修剪也是如此。原生輸出開關 preserveNativeCSS: false 是獨立選項,不論兩個指令如何設定,都會抑制原生輸出。

@preserve native

Master 管理的樣式表若含有不可被原生 CSS 修剪移除的原生 class 規則,請用 @preserve native;。

@import "@master/css";@preserve native;.injected-by-payment-widget {  color: blue;}

樣式表仍由 Master CSS 處理。即使掃描來源未使用那些 class,原生 class 規則仍保留;一般編譯器正規化仍會套用。此政策不會強制將未使用的受管理 class 加入生成輸出,也不代表原始 CSS 位元組會保持不變。

同檔案中 preserve 優先於 prune

兩種情況都啟用原生交付。此政策保留原生 CSS,不會將未使用的受管理 utility 加入安全清單。

文字說明
  1. @preserve native 與 @prune native 同檔案出現時,不論順序都以保留為準。
  2. 因此即使沒有掃描到 widget class,原生 .widget 規則仍保留。
  3. preserveNativeCSS: false 是獨立的輸出開關,可不受兩個指令影響而抑制原生交付。

匯入圖

已解析的 CSS 匯入保留各自的來源邊界,因此每個檔案的政策只適用自己。匯入端不能替相依檔案啟用修剪。pruneNativeCSS: true 下,專案擁有的相依檔案會參與,除非宣告 @preserve native;;套件與遠端樣式不會因為專案匯入它們就變成專案擁有。帶限定匯入保留 media、supports 與 layer 包裝。

src/style.css
@prune native;@import "@master/css";@import "./cards.css";
src/cards.css
@prune native;.card {  border: 1px solid rgb(0 0 0 / 12%);}.unused-card {  color: red;}

掃描來源含有 card 時,輸出保留 .card 並移除 .unused-card。若 cards.css 沒有該指令,兩條規則都會留下。

根節點作用範圍

啟用設定屬於各來源檔案;篩選則使用匯入該檔案的根節點之使用範圍。因此,同一份已啟用的共用檔案在不同根節點下可保留不同 class。由 JavaScript 或框架路由直接匯入的檔案,只有在該匯入進入 Master CSS 管線時,才成為自己的根節點。

根節點預設使用全域掃描器範圍。根節點匯入圖內任何位置的 @source、@source not、@safelist 與 @blocklist 都可縮小或擴大範圍;匯入檔案中的指令會合併到每個匯入它的根節點,相對模式則以宣告檔案為基準解析。

app/a/a.css
@prune native;@import "@master/css";@source "./page.html";@import "../shared.css";
app/shared.css
@prune native;@blocklist "debug-*";.shared-card {  border: 1px solid rgb(0 0 0 / 12%);}.only-used-in-b {  color: blue;}.debug-outline {  outline: 1px solid red;}

若 app/a/page.html 只使用 shared-card,a.css 輸出保留 .shared-card,並移除 .only-used-in-b 與 .debug-outline。另一個根節點 b.css 的頁面若使用 only-used-in-b,則會從同一共用檔案保留該規則。

規則篩選

class 選擇器規則只要有一個選擇器分支中的任一 class 被使用,就會保留;選擇器清單中未使用的分支則移除。檢查依據 class 名稱,不是 DOM 關係:對 .card .title 而言,找到 card 或 title 任一個就會保留分支,因此保留規則仍可能沒有符合的元素。

@prune native;body {  margin: 0;}.card,.unused:hover {  border: 1px solid rgb(0 0 0 / 12%);}@media (width >= 48rem) {  .card .title {    font-weight: 700;  }  .debug-card {    outline: 1px solid red;  }}

使用 card 與 title 時,輸出保留 body、.card 與 .card .title,並移除 .unused:hover 與 .debug-card。body、html、:root 這類沒有 class 選擇器的規則一律保留。修剪後變空的 at-rule 區塊則移除。

修剪依 class 使用情況保留分支

保留標示依循選擇器分支。修剪檢查已使用 class 名稱,不檢查完整選擇器目前是否符合元素。

文字說明
  1. 啟用 @prune native 且使用 card 時,.card, .unused:hover 只保留 .card 分支。
  2. 另一條 .card .title 分支因 card 位於已使用集合而保留,不需要同時找到 title。
  3. 保留不代表 DOM 含有 title 後代。沒有 class 選擇器的規則仍會保留。

manifest 參照

保留的原生規則若讀取 var(--*) 或指定 manifest 動畫名稱,會保留專案入口圖中相符的主題變數與關鍵影格。被移除的規則不會保留任何資源。

@prune native;@import "@master/css";@theme {  --color-primary: #175cff;}.notice {  color: var(--color-primary);}.unused-notice {  color: var(--color-primary);}

若只使用 notice,因 .notice 保留,會輸出 --color-primary;.unused-notice 則移除。

納入的 class

來自有限外部來源的原生 class,可用 @safelist 標為已使用,讓修剪保留其規則。它不取代應用程式在掃描來源中撰寫的 class。

@prune native;@safelist "dialog-open dialog-closing";.dialog-open {  overflow: hidden;}.dialog-closing {  pointer-events: none;}

原生關鍵影格

在原生 CSS 宣告關鍵影格,並在其所屬檔案啟用依使用情況輸出:

@prune native;@theme {  --animate-reveal: reveal .2s ease-out both;}@keyframes reveal {  from {    opacity: 0;  }  to {    opacity: 1;  }}@safelist keyframes "external-enter" "external-exit";@keyframes external-enter {  from {    opacity: 0;  }  to {    opacity: 1;  }}@keyframes external-exit {  from {    opacity: 1;  }  to {    opacity: 0;  }}

關鍵影格與原生 class 規則遵循相同檔案政策。預設是原生 CSS:原位、原樣保留於輸出,Master CSS 絕不再次輸出。@prune native; 讓該檔案的關鍵影格像 token 一樣成為按需定義:它們離開原生輸出,改由使用它們的輸出與生成 CSS 一起輸出。兩個指令同時出現時,@preserve native; 優先。政策只適用宣告檔案,不會透過匯入傳遞。pruneNativeCSS 選項保留專案擁有檔案的範圍。套件 CSS 仍保留,除非明確啟用修剪。preserveNativeCSS: false 抑制原生交付;按需定義仍會在使用處輸出。

關鍵影格使用根節點

動畫 utility、原生動畫宣告、靜態動畫 token 與關鍵影格安全清單會使用對應定義。分析追蹤簡寫值、animation-name 清單、巢狀變數、備援值、具作用範圍的覆寫與遞移 token 相依關係。已使用定義也會使用影格讀取的 token;原生定義只提供這些 token,因為樣式表本身會交付影格。未解析的動態名稱會保守使用編譯圖中所有候選定義,並產生資訊診斷。

動畫名稱可在沒有 class 時保留定義

檔案啟用 @prune native。關鍵影格安全清單保留資源,不會啟動動畫。

文字說明
  1. 在含 @prune native 的檔案中,@safelist keyframes "reveal" 會保留相符的 @keyframes reveal 定義。
  2. 輸出包含關鍵影格定義,但不會僅因安全清單就生成 class 或動畫宣告。
  3. 仍需其他原生宣告或指令碼選取動畫。沒有該使用情況時,單純保留定義不會產生可見動畫效果。

關鍵影格條件與限制

輸出的按需定義會包在原本宣告所處的條件與串接層規則內,包含帶限定匯入的串接層,並排列在生成串接層之後。同一容器內的定義共用一個包裝。同名定義維持獨立並依宣告順序排列,由瀏覽器決定哪個勝出。名稱會解碼且區分大小寫。匿名串接層無法重新進入,因此在匿名層內,或匯入匿名層的按需關鍵影格會造成編譯錯誤;請替串接層命名或保留檔案。影格在原樣式表之外輸出,因此內含相對 url() 會造成編譯錯誤;請使用絕對 URL 或保留檔案。定義 ID 由宣告檔案最近且具有名稱的 package.json,以及套件相對路徑產生,因此同專案從任何檢出或安裝目錄都輸出相同 CSS。重新命名該套件會改變 ID;不在具名套件內的檔案則以絕對路徑作為備援。

關鍵影格資源所有權

影格包含原生宣告;不支援影格內的巢狀指令與 @apply。未使用 utility 與 mixin、已修剪原生規則,以及未交付參照樣式,都不建立使用根節點。@reference 讓定義按需可用,但不匯入其保留政策或安全清單;含相對 URL 的原生關鍵影格仍與樣式表一同交付。專案或基底 manifest 的原生關鍵影格,在不交付它們的樣式表中同樣按需可用。emitted globals 列出輸出以原生或按需方式交付的關鍵影格,讓其他輸出不會重複輸出。只由 JavaScript 引入的動畫名稱,請用 @safelist keyframes "reveal";。

執行階段關鍵影格生命週期

執行階段分別追蹤定義 ID 與名稱,並將按需定義插入自己的樣式表。DOM 移除只有在沒有原生使用、靜態 token 或安全清單繼續使用定義時,才會釋放它。HMR 一起取代定義與 emitted globals;SSR hydration 使用相同 ID 與資源順序。連結樣式表維持一般 CSS,不加入伴隨檔案或標記。

Native keyframes

@keyframes fade-in { to { opacity: 1; } }
產生的 CSS
@keyframes fade-in {  to {    opacity: 1;  }}