CSS 预处理器
问题
什么是 CSS 预处理器?Sass 和 Less 有什么区别?PostCSS 是什么?CSS Modules 怎么用?
答案
预处理器概述
CSS 预处理器扩展了 CSS 语法,增加变量、嵌套、混入、函数等编程能力,编译后生成标准 CSS。
Sass / SCSS
Sass 是最流行的 CSS 预处理器,SCSS 是其兼容 CSS 语法的扩展格式。
变量
variables.scss
$primary: #3b82f6;
$font-size-base: 16px;
$spacing: 8px;
.button {
background: $primary;
font-size: $font-size-base;
padding: $spacing * 2; // 16px
}
嵌套
nesting.scss
.nav {
display: flex;
&__item { // .nav__item(BEM)
padding: 8px 16px;
&--active { // .nav__item--active
color: $primary;
}
&:hover { // .nav__item:hover
background: #f5f5f5;
}
}
// 父选择器引用
.dark & { // .dark .nav
background: #333;
}
}
Mixin(混入)
mixins.scss
// 定义 Mixin
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
@mixin text-ellipsis($lines: 1) {
@if $lines == 1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
@mixin respond-to($breakpoint) {
@if $breakpoint == 'mobile' {
@media (max-width: 767px) { @content; }
} @else if $breakpoint == 'tablet' {
@media (min-width: 768px) and (max-width: 1023px) { @content; }
} @else if $breakpoint == 'desktop' {
@media (min-width: 1024px) { @content; }
}
}
// 使用
.card {
@include flex-center;
}
.title {
@include text-ellipsis(2); // 两行省略
}
.sidebar {
width: 100%;
@include respond-to('desktop') {
width: 300px;
}
}
继承(@extend)
%base-button { // 占位符选择器
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
@extend %base-button;
background: $primary;
color: white;
}
.btn-secondary {
@extend %base-button;
background: #e5e7eb;
color: #333;
}
函数与运算
// 内置函数
$color: darken($primary, 10%); // 加深
$color: lighten($primary, 10%); // 减淡
$color: rgba($primary, 0.5); // 透明度
$color: mix(#f00, #00f, 50%); // 混合
// 自定义函数
@function rem($px, $base: 16) {
@return #{$px / $base}rem;
}
.box {
font-size: rem(14); // 0.875rem
padding: rem(24); // 1.5rem
}
模块系统
@use(推荐,替代 @import)
// _variables.scss
$primary: #3b82f6;
// _mixins.scss
@mixin flex-center { ... }
// main.scss
@use 'variables' as vars;
@use 'mixins';
.button {
background: vars.$primary;
@include mixins.flex-center;
}
Less
Less 语法与 Sass 类似,但使用 @ 作为变量前缀:
style.less
@primary: #3b82f6;
@spacing: 8px;
.button {
background: @primary;
padding: @spacing * 2;
&:hover {
background: darken(@primary, 10%);
}
}
// Mixin
.flex-center() {
display: flex;
justify-content: center;
align-items: center;
}
.card {
.flex-center();
}
Sass vs Less
| 特性 | Sass/SCSS | Less |
|---|---|---|
| 变量前缀 | $ | @ |
| 语法 | .scss(CSS 兼容)/ .sass(缩进) | .less |
| 编译 | Dart Sass(官方推荐) | less.js |
| 条件/循环 | @if、@for、@each、@while | 有限支持 |
| 模块系统 | @use、@forward | 无标准模块 |
| 生态 | 更丰富 | Ant Design 使用 |
| 社区 | Bootstrap、Tailwind 等 | Ant Design |
选择建议
新项目推荐 Sass(SCSS),生态更完善。如果使用 Ant Design 则 Less 更方便。 但在 2024 年,很多项目已经转向 PostCSS + CSS 变量 或 原子化 CSS(Tailwind)。
PostCSS
PostCSS 不是预处理器,而是 CSS 转换工具——通过插件对 CSS 进行处理。
常用插件
| 插件 | 说明 |
|---|---|
| Autoprefixer | 自动添加浏览器前缀 |
| postcss-preset-env | 使用未来 CSS 特性(如嵌套) |
| postcss-px-to-viewport | px 转 vw 移动端适配 |
| postcss-pxtorem | px 转 rem |
| cssnano | CSS 压缩优化 |
| postcss-nesting | CSS 原生嵌套语法 |
postcss.config.ts
export default {
plugins: {
autoprefixer: {},
'postcss-preset-env': {
stage: 2,
features: {
'nesting-rules': true,
},
},
cssnano: process.env.NODE_ENV === 'production' ? {} : false,
},
};
CSS Modules
CSS Modules 自动生成唯一的类名,解决样式冲突问题:
Button.module.css
.button {
background: #3b82f6;
color: white;
padding: 8px 16px;
}
.primary {
background: #3b82f6;
}
.large {
font-size: 18px;
padding: 12px 24px;
}
Button.tsx
import styles from './Button.module.css';
function Button() {
return (
<button className={`${styles.button} ${styles.primary}`}>
Click me
</button>
);
}
编译后:<button class="Button_button_x7sj2 Button_primary_a3kd1">
常见面试问题
Q1: Sass 和 Less 有什么区别?
答案:
核心区别:
- 变量:Sass 用
$,Less 用@ - 编程能力:Sass 更强(完善的条件、循环、模块系统)
- 编译:Sass 用 Dart Sass,Less 用 JavaScript
- 生态:Sass 更主流(Bootstrap、Tailwind),Less 在 Ant Design 中使用
Q2: 为什么需要 CSS 预处理器?
答案:
CSS 本身缺少编程能力,预处理器解决了:
- 代码复用:变量、Mixin 避免重复
- 可维护性:嵌套、模块化组织代码
- 计算能力:函数、运算减少硬编码
- 代码组织:
@use/@import拆分文件
但随着 CSS 原生支持了变量(var())、嵌套(&)和 calc(),预处理器的必要性正在降低。
Q3: PostCSS 和 Sass 是什么关系?
答案:
| Sass | PostCSS | |
|---|---|---|
| 类型 | 预处理器 | CSS 工具平台 |
| 工作方式 | 有自己的语法 | 通过插件处理标准 CSS |
| 能力 | 固定的特性集 | 取决于配置的插件 |
| 关系 | 可以和 PostCSS 配合 | 可以替代预处理器的部分功能 |
常见搭配:Sass 写源码 → PostCSS(Autoprefixer + cssnano)后处理。
Q4: CSS Modules 和 Scoped CSS 有什么区别?
答案:
| 特性 | CSS Modules | Vue Scoped CSS |
|---|---|---|
| 实现方式 | 生成唯一类名 | 添加 data-v-xxx 属性 |
| 框架 | 通用(React/Vue) | Vue 专属 |
| 选择器影响 | 类名完全隔离 | 通过属性选择器作用域限制 |
| 使用方式 | styles.className | <style scoped> |
| 深度穿透 | 无需 | :deep() / ::v-deep |
Q5: @import 和 @use 有什么区别?
答案:
| 特性 | @import(已弃用) | @use |
|---|---|---|
| 作用域 | 全局(变量、Mixin全局可见) | 命名空间隔离 |
| 重复加载 | 每次 @import 都会加载 | 只加载一次 |
| 访问方式 | 直接使用 | namespace.$variable |
| 私有成员 | 不支持 | $_private 以 _ 开头 |
// ❌ @import(弃用)
@import 'variables';
color: $primary; // 全局可见
// ✅ @use(推荐)
@use 'variables' as vars;
color: vars.$primary; // 命名空间隔离
Q6: Autoprefixer 的原理是什么?
答案:
Autoprefixer 是 PostCSS 插件,根据 Can I Use 数据库和项目的 browserslist 配置,自动为 CSS 添加或移除浏览器前缀:
/* 输入 */
.box {
display: flex;
user-select: none;
}
/* 输出 */
.box {
display: -webkit-flex;
display: flex;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
配合 package.json 或 .browserslistrc 中的配置:
{
"browserslist": ["> 1%", "last 2 versions", "not dead"]
}
Q7: Sass 中 @extend 和 @mixin 有什么区别?
答案:
| 特性 | @extend | @mixin |
|---|---|---|
| 参数 | 不支持 | 支持 |
| 编译结果 | 合并选择器 | 复制代码 |
| 输出体积 | 较小(选择器合并) | 较大 |
| 使用场景 | 继承相同样式 | 带参数的复用 |
// @extend → 合并选择器
.btn-primary, .btn-secondary {
padding: 8px 16px;
border-radius: 4px;
}
// @mixin → 复制代码
.card { display: flex; justify-content: center; align-items: center; }
.modal { display: flex; justify-content: center; align-items: center; }
推荐:有参数用 @mixin,纯复用用 @extend(配合占位符 %)。