在这篇文章中,我们将一起深入探讨 CSS 中一个既充满争议又极具历史意义的特性——@apply 规则,并结合 2026 年最新的前端工程化视角,审视样式复用的过去、现在与未来。如果你曾在浏览一些老旧的 CSS 文档或早期的 Web 组件相关教程时遇到过这个神秘的语法,或者在试图使用它时发现浏览器报错,那么这篇文章正是为你准备的。
虽然 @apply 规则本身已是历史尘埃,但它所代表的“组合优于继承”的理念,在今天通过 Tailwind CSS、CSS-in-JS 以及原生 Web Components 的结合,焕发了新的生机。我们将从最基础的概念出发,探索 @apply 的初衷、它曾拥有的辉煌、为什么它最终被主流浏览器移除,以及在现代开发中我们应该采用什么样的高效替代方案。无论你是 CSS 新手还是寻求最佳实践的老手,通过这篇文章,你都将对 CSS 的复用机制有更深刻的理解。
什么是 @apply 规则?
简单来说,@apply 曾是一个被提议用于 CSS 自定义属性(CSS Variables)的规则,旨在模仿预处理器(如 Sass 或 Less)中的“Mixin”或“混合宏”功能。它允许我们将一组已经声明好的 CSS 属性存储在一个变量中,然后通过 @apply 规则将其“应用”到另一个选择器中。
这种设计思路在当时非常具有前瞻性:它试图让原生 CSS 拥有类似编程语言中“函数调用”的能力。我们可以创建一组样式逻辑并将其存储在一个变量中,供以后重复使用,而且可以根据需要随时随地的引用这些属性。这在理论上极大地增强了 CSS 的代码复用能力。
原始语法示例:它原本是如何工作的?
为了让你更直观地理解,让我们看看在当年 Chrome 浏览器曾短暂支持过的版本中,它的语法是怎样的。
注意:以下代码是为了演示历史语法,在现代浏览器中将无法正常运行。
/* 定义部分:在外部页面或根元素中 */
:root {
/* 定义一个包含多个属性的变量 */
--heading-style: {
color: red;
text-decoration: underline;
font-weight: bold;
};
}
/* 使用部分:在具体的组件中 */
.heading {
/* 类似于 JAVA 语言中的方法调用,将变量中的属性“应用”过来 */
@apply --heading-style;
}
在这个阶段,我们只是定义了一组静态的属性,并通过 @apply 将其注入到 .heading 类中。这种写法对于习惯了 Sass 的 @include 或 Less 的 Mixin 语法的开发者来说,显得非常亲切且易于上手。
为什么 @apply 规则最终被移除了?
你可能会问,这么好用的功能,为什么现在的 Chrome 和 Firefox 打开开发者工具时会提示它不识别呢?这其实是一个关于性能、维护性和 CSS 设计哲学的漫长讨论结果。
虽然 @apply 在概念上很吸引人,但在实际落地时,它暴露出了一些致命的缺陷,最终导致 W3C CSS 工作组和浏览器厂商决定放弃它(主要在 2016-2017 年左右)。主要原因包括以下几个方面:
- 静态复用的局限性:通过使用 @apply 规则,我们本质上只能复用那些已经声明好的“静态”属性。它并没有像预处理器那样支持参数传递。这意味着每当你需要稍微修改一点样式(比如将红色改为蓝色),你就不得不创建一个新的变量。
- 变量爆炸与管理噩梦:在实际的大型网页开发中,组件之间的样式千差万别。如果我们无法传递参数来微调样式,开发者就会被迫创建海量的变量来覆盖各种细微的变体(例如 –button-red, –button-blue-large, –button-blue-round)。创建和使用大量缺乏逻辑关联的变量,并不是一种高效的维护方法。
- 循环依赖与解析顺序:原生 CSS 引擎在处理 @apply 时遇到了复杂的解析顺序问题。当一个变量引用另一个变量,或者形成循环引用时,浏览器需要极高的性能开销来计算这些依赖关系。
- 与原生类的冗余:CSS 本身就拥有极其强大的类选择器机制。更高效的方案往往是直接使用普通的 CSS 类,并结合标准的选择器权重来复用样式。
基于这些原因,@apply 规则最终从 CSS 规范草案中被移除了。但是,不用担心,我们依然有更好的方式来达到同样的目的。
2026年视角:深度解析 @apply 的“精神续作”——原子化 CSS 与 AI 辅助开发
在 2026 年,当我们谈论“像 @apply 那样复用代码”时,我们实际上是在谈论原子化 CSS 和 构建时工具 的结合。有趣的是,现在的业界领袖 Tailwind CSS 拥有自己的 @apply 指令,但这与原生的 CSS 提案截然不同——它是通过预处理器在构建阶段将样式“展开”到你的 CSS 文件中的。
在我们的日常开发中,尤其是结合像 Cursor 或 Windsurf 这样的 AI IDE 时,我们发现直接编写原子类比在 CSS 文件内部使用 @apply 组合类要高效得多。为什么?因为 AI 模型(以及人类代码审查者)能直接从 HTML 中看清元素的最终外观,而不需要跳转到 CSS 文件去查找某个抽象语义类的定义。
让我们思考一下这个场景:当你在处理一个复杂的 Bug 时,如果你看到 INLINECODEd63054f2,你瞬间就能理解它的结构。但如果你看到 INLINECODE6261f36d,你就必须去 CSS 文件中查找 .submit-button 到底引用了哪些 Mixin。在 2026 年的高效协作环境中,上下文切换的成本是优化的重点。
示例 1:尝试使用 @apply(已废弃的演示)
为了证明这个规则已经不再工作,让我们编写一个完整的示例。如果你将其复制到本地并在浏览器中打开,你会发现样式并没有被应用,并且控制台可能会报错。
CSS @apply 规则历史演示
/* 定义部分:尝试在根元素中定义样式块变量 */
:root {
/* 这是一个包含属性集合的自定义属性 */
--gfg-style-block: {
background-color: lightgreen;
border-radius: 4px;
border: 1px solid black;
color: green;
padding: 10px;
};
/* 另一个简单的文本样式变量 */
--comp-style-block: {
color: grey;
font-style: italic;
};
}
/* 应用部分:尝试使用 @apply 引入上面的变量 */
.header-box {
/* 这种写法在现代浏览器中会被忽略或报错 */
@apply --gfg-style-block;
}
.sub-header {
@apply --comp-style-block;
}
前端技术演示
深入理解 CSS 规范的演变
代码解析与预期结果:
在上面的代码中,我们尝试定义两个包含 CSS 属性块的变量 INLINECODE2a02a8d2 和 INLINECODE2cdabf52。随后,我们在 INLINECODE07c2bf85 和 INLINECODEfd9a251e 类中使用 @apply 规则来引用它们。
然而,当你运行这段代码时,你会发现页面上的文本没有任何特殊的背景色或边框。这正是因为 INLINECODEb230106d 规则已经从浏览器中移除了。即使语法看起来很完美,浏览器引擎也无法理解 INLINECODE2944a1ba 这个指令,因此它将这些声明视为无效并直接丢弃。
示例 2:现代推荐做法——使用原生的 CSS 类
既然 @apply 已经成为了历史,那么在今天的开发中,我们如何优雅地实现样式的复用呢?答案是最基础、最强大,却也最容易被忽视的工具:CSS 类选择器。
让我们用现代、兼容性最好的方式重写上面的示例。
现代 CSS 类复用最佳实践
/* 定义通用样式类 */
.gfg-box {
background-color: lightgreen;
border-radius: 4px;
border: 1px solid black;
color: green;
padding: 15px; /* 增加内边距使其更好看 */
display: inline-block; /* 让块级元素适应内容宽度 */
}
.comp-text {
color: grey;
font-style: italic;
margin-top: 10px;
}
前端技术演示
深入理解 CSS 规范的演变
现代方案的优势:
在这个例子中,我们将原本要在 @apply 中定义的属性直接写在了 INLINECODE77914f19 和 INLINECODE60aab379 类中。这种方法不仅代码更少,而且性能更高。浏览器对原生类的解析经过了数十年的优化,渲染速度极快。
更重要的是,这种方式允许我们在 HTML 中灵活组合类名。例如,我们可以创建一个 INLINECODE93cdf851 基础类,再创建一个 INLINECODEf5a45e2b 颜色类,将它们应用在同一个按钮上,从而实现模块化开发。
实战进阶:构建灵活的组件系统
为了让你更深入地掌握“后 @apply 时代”的 CSS 写法,让我们通过一个更复杂的例子来展示如何利用 CSS 变量配合类名,构建出类似 @apply 的效果,但却拥有更强的动态能力。
在这个场景中,我们需要创建一个卡片组件,它有不同的尺寸和主题颜色。
进阶:CSS 变量与类组合
/* 1. 定义设计 Token(原子化 CSS 变量) */
:root {
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--color-primary-bg: #e3f2fd;
--color-primary-text: #1565c0;
--color-success-bg: #e8f5e9;
--color-success-text: #2e7d32;
--border-radius: 8px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.1);
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
}
/* 2. 定义基础工具类(类似于 Mixin 的功能) */
.card-base {
border-radius: var(--border-radius);
padding: var(--spacing-md);
border: 1px solid transparent;
box-shadow: var(--shadow-sm);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
/* 3. 定义修饰符类 */
.card-theme-primary {
background-color: var(--color-primary-bg);
color: var(--color-primary-text);
border-color: var(--color-primary-text);
}
.card-theme-success {
background-color: var(--color-success-bg);
color: var(--color-success-text);
border-color: var(--color-success-text);
}
.card-large {
padding: var(--spacing-lg);
box-shadow: var(--shadow-md);
}
/* 4. 交互状态 */
.card-base:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
项目概览
这是一个使用 CSS 类组合构建的卡片组件,没有使用 @apply。
部署成功
通过组合不同的类名,我们可以轻松改变组件的外观。
代码深度解析:
在这个进阶示例中,我们并没有使用 @apply,而是运用了现代 CSS 的核心思想来达到目的:
- 原子化变量:我们在
:root中定义了间距、颜色、阴影等基础变量。这些变量可以被任何类引用,解决了“变量复用”的问题。
- 单一职责类:INLINECODE8a920961 负责结构(圆角、阴影、过渡),而 INLINECODE580784ec 负责配色,
.card-large负责尺寸。这种分离使得代码非常容易维护。
- 组合即复用:我们在 HTML 中通过 INLINECODE1ac0c1c4 来“组合”样式。这比 INLINECODEa4c5649b 更加灵活,因为它是基于 HTML 结构的声明式组合,而不是在 CSS 内部的静态替换。
替代方案对比:Sass/Less 的 Mixin vs 原生 CSS
既然原生的 @apply 走不通,很多团队会回退到使用 CSS 预处理器。这也是一个非常主流且合理的方案。让我们简单对比一下:
- Sass Mixin: 允许你编写带有参数的逻辑。例如
@include button-style(blue, large)。这非常强大,因为它会生成具体的 CSS 代码。 - 原生 CSS 类: 不支持参数。但你可以通过 CSS 变量(CSS Variables)来实现类似的效果,比如定义
--color: blue。
最佳实践建议:
如果你的项目非常复杂,需要大量的逻辑计算,那么使用 Sass 等预处理器依然是最高效的选择。但如果你希望保持代码的简洁,不依赖构建工具,那么“原生 CSS 变量 + 多类名组合” 是目前最推荐的“原生 @apply”替代品。
性能优化与常见错误
在追求代码复用的过程中,我们常常会犯一些错误。以下是一些实用的见解和优化建议:
- 避免过度抽象:不要为了复用而复用。如果你发现为了复用一个样式,需要写 5 个不同的类名,那么直接把样式写在当前类里可能更清晰。CSS 的可读性同样重要。
- CSS 变量的性能:虽然 CSS 变量很强大,但切记不要在关键渲染路径(如动画、滚动等高频触发的事件)中过度依赖复杂的 CSS 变量计算。因为在某些旧版浏览器中,这可能会导致重绘性能下降。
- 选择器权重问题:使用原生类复用时,要注意选择器的特异性。尽量保持类选择器的权重在一个水平线上,避免出现需要
!important来覆盖样式的尴尬局面。
总结
虽然 CSS 的 @apply 规则作为一个历史特性已经离我们而去,但它留下的思考——即如何更好地在原生 CSS 中复用样式逻辑——推动了 CSS 变量和 CSS 模块的进化。
在这篇文章中,我们学习了:
- @apply 的原始语法和它试图解决的问题。
- 它为什么因为性能和维护性的原因被废弃。
- 如何使用现代的 CSS 类组合 和 CSS 自定义属性 来替代它,构建更健壮的系统。
- 在 2026 年的技术背景下,为什么原子化和声明式的组合方式更符合 AI 辅助编程和高效协作的需求。
现在,你已经掌握了编写整洁、可维护且高性能 CSS 代码的秘诀。下次当你想要“复制粘贴”一段样式时,不妨试着创建一个语义清晰的 CSS 类,或者利用 CSS 变量来管理你的设计 Token。这不仅会让你的代码更专业,也会让未来的维护者(包括你自己)感激不尽。
希望这篇文章能帮助你更好地理解 CSS 的演变,并能在实际项目中灵活运用这些现代开发技巧。