2024 年,你能放弃 CSS 预处理器吗?
Category(分类): CSS Status: 未知
本文以 2024 年的前端生态为背景,保留原文关于 CSS 预处理器和原生 CSS 的主要内容,并修正已经过时、错误或由网页复制产生的内容。浏览器支持情况会继续变化,实际项目应结合目标浏览器和构建工具验证。
在 2024 年,完全抛弃 CSS 预处理器可能仍然不是所有项目的最佳选择。但可以预见的是,随着原生 CSS 的不断进化,预处理器的重要性正在逐渐降低。更稳妥的做法是在项目中逐步引入原生 CSS 新特性,同时根据项目需求决定是否保留预处理器,以获得合适的开发体验、构建效率和浏览器兼容性。
随着前端技术的发展,CSS 预处理器已经在很多项目中使用了很长时间。然而,原生 CSS 的能力也在持续增强,一个值得思考的问题是:现在我们是否还需要 CSS 预处理器?
CSS 预处理器的兴起与发展
CSS 预处理器诞生于原生 CSS 能力相对有限的年代。Sass 创建于 2006 年,最初的实现是 Ruby Sass;Stylus 在 2010 年前后开始发展,并于 2011 年发布早期版本。它们为开发者带来了更灵活的样式编写体验,也提升了大型样式表的组织能力。
需要注意:Ruby Sass 已于 2019 年停止维护,当前 Sass 官方推荐使用 Dart Sass。node-sass 也已经废弃,不应再作为新项目的首选安装方案。
主流的 CSS 预处理器包括:
- Sass / SCSS:生态成熟,功能丰富。当前应优先使用 Dart Sass;
- Less:语法接近 CSS,学习成本相对较低;
- Stylus:来自 Node.js 生态,语法较为自由,但现代项目中的使用比例已经降低。
这些预处理器通常提供以下能力:
- 变量(Variables);
- 混合(Mixin);
- 嵌套规则(Nesting);
- 模块化(Modules);
- 函数、循环和条件判断;
- 在构建阶段生成兼容性更好的 CSS。
不过,最后一项需要特别说明:预处理器本身不会自动解决所有浏览器兼容问题。自动添加厂商前缀通常由 Autoprefixer、PostCSS 和 Browserslist 等工具负责。
Sass 变量(Variables)
$font-size: 10px;
$font-family: Helvetica, sans-serif;
body {
font: $font-size $font-family;
}
.mark {
font-size: 1.5 * $font-size;
}
Sass 混合(Mixin)
@mixin clearfix {
&::after {
display: table;
content: '';
clear: both;
}
}
.sidebar {
@include clearfix;
}
Sass 嵌套(Nesting)
// menu
.nav {
> li {
> a:hover {
background-color: red;
}
}
}
Sass 模块(Modules)
旧项目中经常使用 @import:
// Legacy Sass syntax(旧版写法)
@import './common';
@import './github-markdown';
@import './mixin';
@import './variables';
但 Sass 的 @import 已被弃用,新项目应使用 @use 和 @forward:
@use './common';
@use './github-markdown';
@use './mixin';
@use './variables';
@use 默认会创建模块命名空间,可以减少全局变量和成员名称冲突。
CSS 预处理器的局限性
尽管 CSS 预处理器带来了诸多便利,但它们也存在一些问题。
1. 需要额外的编译配置
在编写样式之前,还需要配置构建工具和编译流程。过去的 node-sass 依赖原生模块,可能因为 Node.js 版本、操作系统或编译环境不同而安装失败;现在应优先选择安装和维护更简单的 Dart Sass。
现代 Vite、Webpack、Rspack 等工具通常已经集成了 Sass、Less 等处理流程,但项目仍然需要添加对应依赖和配置。

2. 编译过程会消耗时间和资源
预处理器需要在构建阶段把 Sass、Less 或 Stylus 转换成浏览器可以直接解析的 CSS。生产构建必然会增加一个处理步骤,开发时也可能影响启动和热更新速度。
不过,这个问题不能绝对化:现代工具通常会使用增量编译、缓存和按需处理,简单样式表的编译开销可能很小。真正需要关注的是项目规模、依赖数量、Source Map 生成和构建配置。

3. 不同预处理器的语法差异会增加学习成本
Sass、Less 和 Stylus 的变量、Mixin、插值和模块语法并不相同。同一个团队甚至同一个项目中,如果同时使用多种预处理器,维护成本会进一步增加。
下面是 Sass 和 Less 的变量、Mixin 语法对比:
// Sass / SCSS
$color: #f00;
$images: '../img';
@mixin clearfix {
&::after {
content: '';
display: table;
clear: both;
}
}
body {
color: $color;
background: url("#{$images}/1.png");
@include clearfix;
}
// Less
@color: #f00;
@images: '../img';
.clearfix() {
&::after {
content: '';
display: table;
clear: both;
}
}
body {
color: @color;
background: url("@{images}/1.png");
.clearfix();
}
原文中的 Sass #{img} 和 Less @{img} 都引用了未定义的变量,已修正为 $images 和 @images。
4. 调试困难,尤其是在复杂项目中
预处理器最终会生成另一份 CSS。浏览器 DevTools 通常通过 Source Map 将编译后的规则映射回 Sass 或 Less 源文件,但复杂嵌套、Mixin 展开、变量计算和多级构建链仍可能让调试变得困难。
调试时应注意:
- 开发环境开启 Source Map;
- 生产环境根据安全和体积需求决定是否发布 Source Map;
- 检查浏览器加载的到底是源文件还是编译产物;
- 尽量减少过深的嵌套和难以追踪的 Mixin;
- 不要把所有样式逻辑都隐藏在预处理器函数中。

原生 CSS 的进化
与此同时,CSS 标准也在不断发展。CSS 不再只是“写几条固定样式”,而是逐渐拥有了变量、计算、嵌套、容器查询、条件规则和更强的选择器能力。
CSS 自定义属性(Custom Properties)
CSS 自定义属性可以在运行时参与级联和继承,并通过 var() 读取。它们与 Sass 变量有一个重要区别:Sass 变量在构建阶段被替换,CSS 自定义属性会保留到浏览器运行时。
:root {
--main-color: #ff00ff;
--main-bg: rgb(200 255 255);
--logo-border-color: rebeccapurple;
--header-height: 68px;
--content-padding: 10px 20px;
--base-line-height: 1.428571429;
--transition-duration: 0.35s;
--external-link: 'external link';
--margin-top: calc(2vh + 20px);
}
body {
color: var(--main-color);
background-color: var(--main-bg);
line-height: var(--base-line-height);
}
原文中用于说明 --VAR_NAME: <declaration-value> 的伪代码不能直接当作 CSS 运行。自定义属性必须写在声明块中,<declaration-value> 只是规范中的占位表示。
CSS 自定义属性不仅可以在样式表中使用,还可以通过 JavaScript 动态修改,这为主题切换提供了便利:
document.documentElement.style.setProperty(
'--primary-color',
'#e74c3c'
)
calc() 函数
calc() 可以在 CSS 中进行带单位的计算:
.container {
width: calc(100% - 20px);
padding-inline: max(16px, 5vw);
}
calc() 不能代替所有预处理器计算。例如,某些需要循环、条件判断或构建阶段生成大量选择器的场景,仍然需要预处理器或其他构建工具。
使用 CSS 变量切换主题
html {
--hue: 210;
--text-color-normal: hsl(var(--hue) 77% 17%);
--page-background: hsl(210 30% 98%);
}
html[data-theme='dark'] {
--text-color-normal: hsl(var(--hue) 10% 82%);
--page-background: hsl(210 20% 12%);
}
body {
color: var(--text-color-normal);
background: var(--page-background);
}
通过 JavaScript 更改元素属性即可切换主题:
document.documentElement.setAttribute('data-theme', 'dark')
document.documentElement.setAttribute('data-theme', 'light')
生产项目还应保存用户的主题选择,并考虑 prefers-color-scheme、页面初始闪烁和 SSR hydration 一致性。
原生 CSS 中的 Mixin?
原文提到过 CSS @apply 规则。它曾经是一个提案,目的是把一组声明存进自定义属性,再通过 @apply 复用:
/* Historical proposal(历史提案,不是当前可用的原生 CSS) */
:root {
--pink-schema: {
color: #6a8759;
background-color: #f64778;
};
}
body {
@apply --pink-schema;
}
这个旧提案后来被放弃,现代浏览器不能依赖它。Tailwind 等工具中的 @apply 是构建工具语法,也不是浏览器原生实现。
当前可以根据场景选择以下替代方式:
- 使用 CSS 自定义属性复用值;
- 使用语义化类名或工具类;
- 使用
@layer管理样式层级; - 使用 Sass Mixin 完成构建阶段的样式复用;
- 关注仍处于草案阶段的 CSS Custom Functions and Mixins,但不要把草案当作稳定浏览器能力。
原生 CSS 的未来
原生 CSS 目前已经可以替代预处理器的很多常用功能,但还不能在所有项目中完全替代 Sass、Less 或 Stylus。
CSS Nesting
CSS Nesting Module 曾经是提案,但到 2024 年已经在现代 Chrome、Edge、Firefox 和 Safari 中得到较广泛支持。它可以写出类似预处理器的嵌套语法:
.card {
background: white;
& .title {
color: black;
}
&:hover {
box-shadow: 0 8px 24px rgb(0 0 0 / 15%);
}
}
使用时仍应确认项目的最低浏览器版本。对于较旧浏览器,可以通过 PostCSS 等工具转换,或使用更传统的扁平选择器:
.card {
background: white;
}
.card .title {
color: black;
}
容器查询和 :has()
@container 可以根据组件容器的尺寸,而不是整个视口尺寸应用样式:
.card-list {
container-type: inline-size;
}
@container (min-width: 640px) {
.card {
display: grid;
grid-template-columns: 160px 1fr;
}
}
:has() 是一种关系选择器,可以根据后代或兄弟元素的存在选择父级或相关元素:
.form-group:has(:user-invalid) {
border-color: crimson;
}
这些能力减少了部分“必须通过 JavaScript 或预处理器生成样式”的场景,但仍需要考虑浏览器支持、性能和可维护性。
其他值得关注的原生能力
@layer:管理级联层级,减少选择器权重竞争;clamp()、min()和max():实现响应式尺寸计算;color-mix():在 CSS 中混合颜色;@property:为部分自定义属性声明类型、初始值和继承行为;:is()、:where():简化复杂选择器并控制选择器权重;- CSS Logical Properties:使用
margin-inline、padding-block等属性支持不同书写方向。
现在可以放弃 CSS 预处理器吗?
这没有统一答案,应该根据项目需求决定。
可以考虑不使用预处理器的情况
- 项目只需要变量、主题切换和运行时配置;
- 项目主要面向现代浏览器;
- 样式结构不复杂,嵌套层级较浅;
- 团队希望减少构建依赖和配置;
- 项目已经使用 CSS Modules、原生 CSS 或其他组件级样式方案;
- 希望让浏览器 DevTools 直接对应实际 CSS 文件。
仍然适合使用预处理器的情况
- 需要兼容不支持现代 CSS 的旧浏览器;
- 大量使用构建阶段的 Mixin、函数、循环和条件判断;
- 需要根据设计令牌批量生成样式;
- 已经拥有成熟的 Sass 设计系统和团队工作流;
- 原生 CSS 与构建工具暂时无法满足项目的模块化需求。
一个渐进式迁移方案
- 先统计项目实际使用了哪些 Sass/Less 特性;
- 将简单变量逐步迁移为 CSS 自定义属性;
- 将简单嵌套迁移为原生 CSS Nesting,或保持扁平选择器;
- 用
@layer、CSS Modules 或组件边界管理样式; - 保留复杂函数、循环和确实有价值的 Mixin;
- 删除不再使用的预处理器依赖和构建配置;
- 使用目标浏览器列表进行构建和回归测试。
不要为了“追求原生 CSS”而一次性重写所有样式。迁移应该以减少复杂度、改善维护体验和满足浏览器支持为目标。
结语
在 2024 年,完全抛弃 CSS 预处理器可能仍然不是所有项目的最佳选择。但可以预见,随着原生 CSS 的不断进化,预处理器的重要性会逐渐降低。
对于只使用变量、简单嵌套和基础计算的项目,可以优先尝试原生 CSS。对于依赖复杂 Mixin、函数、循环、条件判断和大规模代码生成的项目,Sass 或 Less 仍然有价值。
未来,我们可能会看到更多像 PostCSS 这样的工具,根据项目需求灵活填补原生 CSS 与预处理器之间的差距。不过,PostCSS 更准确地说是一个 CSS 转换平台,是否具备预处理能力取决于所使用的插件。
无论是否继续使用预处理器,持续关注 CSS 标准的发展,并在实践中逐步尝试新特性,都是前端开发者需要保持的能力。
总结
- CSS 预处理器仍有价值,但不再是所有项目的必需品;
- CSS 自定义属性、
calc()、CSS Nesting、容器查询和:has()已经覆盖了许多过去依赖预处理器的场景; - Sass 的
@import已弃用,新项目应使用@use和@forward; node-sass和 Ruby Sass 已退出维护,当前应优先使用 Dart Sass;- CSS
@apply的旧提案已经放弃,工具链中的同名语法不能当作原生 CSS; - 预处理器本身不会自动解决浏览器兼容问题,兼容处理通常由 PostCSS、Autoprefixer 和 Browserslist 等工具完成;
- 是否放弃预处理器,应结合浏览器支持、项目复杂度、团队经验和构建成本决定。