技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTCSS

2024 年,你能放弃 CSS 预处理器吗?

所属馆藏
CSS
文件格式
Markdown
原始路径
CSS/01-2024 年,你能放弃 CSS 预处理器吗?
本文目录9 个章节
  1. CSS 预处理器的兴起与发展
  2. CSS 预处理器的局限性
  3. 原生 CSS 的进化
  4. 原生 CSS 中的 Mixin?
  5. 原生 CSS 的未来
  6. 现在可以放弃 CSS 预处理器吗?
  7. 结语
  8. 总结
  9. 参考资料

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 预处理器包括:

  1. Sass / SCSS:生态成熟,功能丰富。当前应优先使用 Dart Sass;
  2. Less:语法接近 CSS,学习成本相对较低;
  3. 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 等处理流程,但项目仍然需要添加对应依赖和配置。

CSS 预处理器配置示意图

2. 编译过程会消耗时间和资源

预处理器需要在构建阶段把 Sass、Less 或 Stylus 转换成浏览器可以直接解析的 CSS。生产构建必然会增加一个处理步骤,开发时也可能影响启动和热更新速度。

不过,这个问题不能绝对化:现代工具通常会使用增量编译、缓存和按需处理,简单样式表的编译开销可能很小。真正需要关注的是项目规模、依赖数量、Source Map 生成和构建配置。

CSS 预处理器构建过程示意图

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 预处理器 Source Map 调试示意图

原生 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-inlinepadding-block 等属性支持不同书写方向。

现在可以放弃 CSS 预处理器吗?

这没有统一答案,应该根据项目需求决定。

可以考虑不使用预处理器的情况

  • 项目只需要变量、主题切换和运行时配置;
  • 项目主要面向现代浏览器;
  • 样式结构不复杂,嵌套层级较浅;
  • 团队希望减少构建依赖和配置;
  • 项目已经使用 CSS Modules、原生 CSS 或其他组件级样式方案;
  • 希望让浏览器 DevTools 直接对应实际 CSS 文件。

仍然适合使用预处理器的情况

  • 需要兼容不支持现代 CSS 的旧浏览器;
  • 大量使用构建阶段的 Mixin、函数、循环和条件判断;
  • 需要根据设计令牌批量生成样式;
  • 已经拥有成熟的 Sass 设计系统和团队工作流;
  • 原生 CSS 与构建工具暂时无法满足项目的模块化需求。

一个渐进式迁移方案

  1. 先统计项目实际使用了哪些 Sass/Less 特性;
  2. 将简单变量逐步迁移为 CSS 自定义属性;
  3. 将简单嵌套迁移为原生 CSS Nesting,或保持扁平选择器;
  4. @layer、CSS Modules 或组件边界管理样式;
  5. 保留复杂函数、循环和确实有价值的 Mixin;
  6. 删除不再使用的预处理器依赖和构建配置;
  7. 使用目标浏览器列表进行构建和回归测试。

不要为了“追求原生 CSS”而一次性重写所有样式。迁移应该以减少复杂度、改善维护体验和满足浏览器支持为目标。

结语

在 2024 年,完全抛弃 CSS 预处理器可能仍然不是所有项目的最佳选择。但可以预见,随着原生 CSS 的不断进化,预处理器的重要性会逐渐降低。

对于只使用变量、简单嵌套和基础计算的项目,可以优先尝试原生 CSS。对于依赖复杂 Mixin、函数、循环、条件判断和大规模代码生成的项目,Sass 或 Less 仍然有价值。

未来,我们可能会看到更多像 PostCSS 这样的工具,根据项目需求灵活填补原生 CSS 与预处理器之间的差距。不过,PostCSS 更准确地说是一个 CSS 转换平台,是否具备预处理能力取决于所使用的插件。

无论是否继续使用预处理器,持续关注 CSS 标准的发展,并在实践中逐步尝试新特性,都是前端开发者需要保持的能力。

总结

  1. CSS 预处理器仍有价值,但不再是所有项目的必需品;
  2. CSS 自定义属性、calc()、CSS Nesting、容器查询和 :has() 已经覆盖了许多过去依赖预处理器的场景;
  3. Sass 的 @import 已弃用,新项目应使用 @use@forward
  4. node-sass 和 Ruby Sass 已退出维护,当前应优先使用 Dart Sass;
  5. CSS @apply 的旧提案已经放弃,工具链中的同名语法不能当作原生 CSS;
  6. 预处理器本身不会自动解决浏览器兼容问题,兼容处理通常由 PostCSS、Autoprefixer 和 Browserslist 等工具完成;
  7. 是否放弃预处理器,应结合浏览器支持、项目复杂度、团队经验和构建成本决定。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS