网站导航

Directives

原生 CSS 裁剪

控制每份样式表中的原生类裁剪和按需生成的关键帧。

本页内容

原生 CSS 裁剪移除未使用的类选择器分支,并使关键帧按需可用。这是样式表策略,与工具类生成分开。优化 CSS 交付说明何时启用;扫描指令定义源与候选筛选。

裁剪指令止于文件边界

此次编译仅使用 local。app.css 启用,cards.css 未启用。导入 cards.css 不会将裁剪指令传入其中。

文字说明
  1. app.css 包含 @prune native、导入 cards.css,并定义 .local 和 .unused-local。唯一使用的类是 local。
  2. 其输出保留 .local,移除 .unused-local。
  3. cards.css 未声明裁剪或保留指令。即使两个类均未使用,默认策略仍保留 .card 和 .unused-card。
  4. 全项目 pruneNativeCSS 选项可另行让项目文件参与裁剪。@preserve native 在其所属文件内优先。本例单独呈现显式逐文件策略。

原生 CSS 默认保留,即使提供类列表也一样。在集成或样式表 API 中设置 pruneNativeCSS: true,可裁剪项目拥有的源。包 CSS 仍保留,除非其源显式启用裁剪。

@prune native

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

此指令仅对声明文件启用裁剪,不会传递到导入样式表。同文件中的 @preserve native; 优先,即使集成启用全项目裁剪也一样。原生输出开关 preserveNativeCSS: false 是独立选项,无论两个指令如何设置,均抑制原生输出。

@preserve native

Master 管理的样式表若包含不可被原生 CSS 裁剪移除的原生类规则,请用 @preserve native;。

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

样式表仍由 Master CSS 处理。即使扫描源未使用这些类,原生类规则仍保留;常规编译器规范化仍会应用。此策略不会强制将未使用的受管理类加入生成输出,也不代表保留原始 CSS 字节。

同文件中 preserve 优先于 prune

两种情况均启用原生交付。此策略保留原生 CSS,不会将未使用受管理工具类加入安全列表。

文字说明
  1. @preserve native 与 @prune native 同文件出现时,无论顺序均以保留为准。
  2. 因此即使没有扫描到 widget 类,原生 .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 没有该指令,两条规则均保留。

根节点作用域

启用设置属于各源文件;筛选使用导入该文件的根节点之使用范围。因此同一份已启用共享文件可在不同根节点下保留不同类。由 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,则从同一共享文件保留该规则。

规则筛选

类选择器规则只要有一条分支中的任一类被使用就会保留;选择器列表中未使用分支则被移除。检查依据是类名而非 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 这类没有类选择器的规则始终保留。裁剪后变空的 at-rule 块则移除。

裁剪按类使用情况保留分支

保留高亮遵循选择器分支。裁剪检查已使用类名,不检查完整选择器当前是否匹配元素。

文字说明
  1. 启用 @prune native 且使用 card 时,.card, .unused:hover 仅保留 .card 分支。
  2. 另一条 .card .title 分支因 card 位于已使用集合而保留,无须同时找到 title。
  3. 保留不代表 DOM 含有 title 后代。没有类选择器的规则仍会保留。

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 则被移除。

包含的类

来自有限外部来源的原生类可用 @safelist 标为已使用,使裁剪保留其规则。它不替代应用在扫描源中编写的类。

@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;  }}

关键帧与原生类规则遵循相同文件策略。默认是原生 CSS:在输出中原位、原样保留,Master CSS 绝不再次输出。@prune native; 使该文件的关键帧像令牌一样成为按需定义:它们离开原生输出,改由使用它们的输出与生成 CSS 一起输出。两个指令同时出现时,@preserve native; 优先。策略仅适用于声明文件,不通过导入传递。pruneNativeCSS 选项保留项目拥有文件的范围。包 CSS 仍保留,除非显式启用裁剪。preserveNativeCSS: false 抑制原生交付;按需定义仍在使用处输出。

关键帧使用根节点

动画工具类、原生动画声明、静态动画令牌和关键帧安全列表使用对应定义。分析追踪简写值、animation-name 列表、嵌套变量、回退值、限定作用域的覆盖及传递令牌依赖。已使用定义也会使用帧读取的令牌;原生定义仅提供这些令牌,因为样式表自身交付帧。未解析的动态名称保守使用编译图中所有候选定义,并产生信息诊断。

动画名称可在没有类时保留定义

文件启用 @prune native。关键帧安全列表保留资源,不会启动动画。

文字说明
  1. 在含 @prune native 的文件中,@safelist keyframes "reveal" 保留匹配的 @keyframes reveal 定义。
  2. 输出包含关键帧定义,但不会仅因安全列表就生成类或动画声明。
  3. 仍需其他原生声明或脚本选择动画。没有该使用情况时,单纯保留定义不会产生可见动画效果。

关键帧条件与限制

输出的按需定义包在原声明位置的条件和层规则内,包括带限定导入的层,并位于生成层之后。同一容器内的定义共用一个包装。同名定义保持独立并按声明顺序排列,由浏览器决定胜出的定义。名称会解码且区分大小写。匿名层无法重新进入,因此位于匿名层内或导入匿名层的按需关键帧会导致编译错误;请为层命名或保留文件。帧在原样式表之外输出,因此其中的相对 url() 会导致编译错误;请使用绝对 URL 或保留文件。定义 ID 来自声明文件最近且具有名称的 package.json 及包相对路径,因此同项目从任何检出或安装目录均输出相同 CSS。重命名该包会改变 ID;不在命名包内的文件则回退至绝对路径。

关键帧资源所有权

帧包含原生声明;不支持帧内嵌套指令和 @apply。未使用工具类和混入、已裁剪原生规则及未交付引用样式均不创建使用根节点。@reference 使定义按需可用,但不导入其保留策略或安全列表;含相对 URL 的原生关键帧仍随样式表交付。项目或基础 manifest 的原生关键帧,在不交付它们的样式表中同样按需可用。emitted globals 列出输出以原生或按需方式交付的关键帧,使其他输出不会重复输出。仅由 JavaScript 引入的动画名称,请使用 @safelist keyframes "reveal";。

运行时关键帧生命周期

运行时分别追踪定义 ID 与名称,并将按需定义插入自身样式表。DOM 移除仅在没有原生使用、静态令牌或安全列表继续使用该定义时才释放它。HMR 一同替换定义与 emitted globals;SSR hydration 使用相同 ID 和资源顺序。链接样式表保持常规 CSS,不添加伴随文件或标记。

Native keyframes

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