第二章:编写可复用的模式:@mixin 与 @extend 的战略
第二章:编写可复用的模式:@mixin 与 @extend 的战略
Prorise第二章:编写可复用的模式:@mixin 与 @extend 的战略
摘要: 在上一章中,我们掌握了 SCSS 的核心语法与模块化组织方式。现在,我们将进入一个更高级的领域:代码复用。本章将深度剖析 SCSS 中两种强大的代码复用机制——混合宏 (@mixin) 与 继承 (@extend)。我们将不仅仅学习它们的语法,更重要的是,通过深度的对比与实战,理解它们在编译结果、性能和维护性上的本质区别,并最终形成一套符合 2025 年架构思想的战略选型方案。
在上一章中,我们已经搭建好了清晰的 7-1 Pattern 项目结构。本章我们将要学习的 @mixin 等抽象工具,其最佳实践位置正是在 scss/abstracts/ 目录下。
1 | scss/ |
2.1. 混合宏 (@mixin):动态的样式“函数”
`@mixin` 是 SCSS 中最重要、最灵活的代码复用机制。您可以将它理解为一个可以重复调用的“样式函数”,它能够接收参数,并动态生成样式代码。2.1.1. 定义 (@mixin) 与调用 (@include)
痛点背景: 在 CSS 中,我们经常需要编写一些重复的样式片段,比如清除浮动、处理浏览器厂商前缀等。每次都手动复制粘贴不仅效率低下,而且难以维护。
解决方案: 使用 @mixin 将这些重复的样式块封装起来,在需要的地方通过 @include 调用。
文件路径: scss/abstracts/_mixins.scss
1 | // 定义一个清除浮动的 mixin |
文件路径: scss/components/_card.scss
1 | @use '../abstracts/mixins' as m; // 导入 mixins 模块,并设置别名为 m |
1
2
3
4
5
6
7
8
.card {
border: 1px solid #ccc;
}
.card::after {
content: "";
display: table;
clear: both;
}
@include 的工作方式非常直观:它会将 @mixin 中的代码 原封不动地复制 到调用的位置。
2.1.2. 传递参数:默认值与任意参数 (...)
@mixin 的真正威力在于其接收参数的能力,这让我们的样式封装变得极具动态性。
痛点背景: 假设我们需要创建不同尺寸、不同颜色的按钮。如果为每一种组合都写一个类,代码会变得非常冗余。
解决方案: 创建一个灵活的 button-style mixin,通过传递参数来动态生成按钮样式。
文件路径: scss/abstracts/_mixins.scss
1 | // 定义一个强大的按钮样式 mixin |
文件路径: scss/components/_button.scss
1 | @use '../abstracts/mixins' as m; |
我们可以在 index.html 中引入我们编译好的 main.css,在页面上看到效果
1 |
|

2.1.3. 传递内容块 (@content):创建高阶混合宏
@content 指令允许我们在调用 @mixin 时,向其内部传递一整个样式块。这在创建用于媒体查询、状态嵌套等场景的“包装型” mixin 时极为有用。
痛点背景: 在编写响应式样式时,我们常常需要将同一个组件的样式分散在多个 @media 查询块中,这破坏了组件样式的内聚性。
解决方案: 创建一个响应式断点的 mixin,使用 @content 将特定断点的样式包裹起来。
文件路径: scss/abstracts/_mixins.scss
1 | // 定义一个用于处理小于指定宽度的媒体查询 mixin |
文件路径: scss/components/_card.scss
1 | @use "../abstracts/mixins" as m; |
我们可以在 index.html 中引入我们编译好的 main.css,在页面上看到效果
1 |
|

2.2. 继承 (@extend):共享样式的选择器分组
`@extend` 是另一种代码复用机制,但它的工作原理与 `@mixin` 完全不同。它不是复制代码,而是让一个选择器去**继承**另一个选择器的所有样式,并通过 "选择器分组" 的方式来实现。2.2.1. 基础继承语法
痛点背景: 在开发中,我们经常需要创建一系列相似的组件,它们共享基础样式但又有各自的特色。比如不同类型的提示框(成功、警告、错误、信息),它们的基本结构相同,只是颜色不同。如果每个都单独写样式,会产生大量重复代码。
解决方案: 使用 @extend 让多个选择器继承同一个基础选择器的样式,SCSS 会智能地将它们组合成选择器组,避免代码重复。
文件路径: scss/components/_alert.scss (新增)
1 | .alert { |
文件路径: scss/main.scss
1 | // 在 main.scss 中添加导入 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
.alert, .alert-success, .alert-danger, .alert-warning, .alert-info {
padding: 15px;
border: 1px solid #ccc;
border-radius: 4px;
margin: 10px 0;
font-size: 14px;
}
.alert-success {
background-color: #d4edda;
border-color: #c3e6cb;
color: #155724;
}
.alert-danger {
background-color: #f8d7da;
border-color: #f5c6cb;
color: #721c24;
}
.alert-warning {
background-color: #fff3cd;
border-color: #ffeaa7;
color: #856404;
}
.alert-info {
background-color: #d1ecf1;
border-color: #bee5eb;
color: #0c5460;
}
@extend 的工作方式:SCSS 会将所有继承同一个选择器的类组合成一个 选择器组,基础样式只出现一次,这样既避免了代码重复,又保持了 CSS 的简洁性。
我们可以在 index.html 中测试这些 alert 组件的效果:
1 |
|

注意: @extend 会将调用者的选择器(如 .alert-success)“附加” 到被继承选择器(.alert)出现的所有地方,这可能导致意料之外的样式级联问题,并使编译后的 CSS 选择器关系变得复杂。因此,直接继承一个具体的类(.class)被认为是一种 高风险 的做法。
2.3. 占位符选择器 (%):最佳实践
为了解决 @extend 的滥用风险,SCSS 提供了一种特殊的选择器:占位符选择器(%placeholder)。
核心特性:
- 以
%开头。 - 它本身 不会 被编译到 CSS 文件中,除非它被
@extend。 - 它就像一个 “虚拟” 的规则集,专门用于被继承。
这使得 @extend 变得安全可控,因为它不会污染全局的类选择器。
2.3.1. 对比普通类选择器 vs 占位符选择器
痛点背景: 在之前的例子中,我们使用 .alert 作为基础类,但这意味着即使我们不需要单独使用 .alert,它也会出现在编译后的 CSS 中,增加了文件大小,并可能在 HTML 中被误用。
解决方案: 使用占位符选择器 %alert-base 替代普通的 .alert 类,它只在被继承时才会生成 CSS 代码。
使用普通类选择器(不推荐)
文件路径: scss/components/_alert.scss
1 | .alert { |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 即使不需要单独使用 .alert,它也会出现在编译后的 CSS 中 */
.alert, .alert-success {
padding: 15px;
border: 1px solid #ccc;
border-radius: 4px;
margin: 10px 0;
font-size: 14px;
}
.alert-success {
background-color: #d4edda;
border-color: #c3e6cb;
color: #155724;
}
使用占位符选择器(推荐)
文件路径: scss/components/_alert.scss
1 | // 使用占位符选择器定义基础样式 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* %alert-base 不会出现在编译后的 CSS 中 */
.alert-success, .alert-danger, .alert-warning, .alert-info {
padding: 15px;
border: 1px solid #ccc;
border-radius: 4px;
margin: 10px 0;
font-size: 14px;
}
.alert-success {
background-color: #d4edda;
border-color: #c3e6cb;
color: #155724;
}
.alert-danger {
background-color: #f8d7da;
border-color: #f5c6cb;
color: #721c24;
}
.alert-warning {
background-color: #fff3cd;
border-color: #ffeaa7;
color: #856404;
}
.alert-info {
background-color: #d1ecf1;
border-color: #bee5eb;
color: #0c5460;
}
最佳实践: 始终使用占位符选择器 (%placeholder) 配合 @extend,而不是直接继承普通的类选择器。这样可以确保你的 CSS 更加精简、安全,并且不会产生意外的副作用。
2.4. 核心战略抉择:
现在我们已经了解了两种复用机制,是时候进行一场终极对决了。理解它们的本质区别,是成为 SCSS 高手的关键。
@mixin 和 @extend 最核心的区别在于它们编译后的产物。
SCSS 源码:
1 | // 使用 @mixin |
@mixin 的编译结果:
1 | .widget { |
@extend 的编译结果 (选择器分组):
1 | .widget, .panel { |
| 特性 | @mixin | @extend |
|---|---|---|
| 核心机制 | 代码复制 | 选择器分组 |
| 优点 | 1. 接收参数,高度灵活 2. 安全可预测,不改变选择器结构 3. 清晰独立,每个规则集都是完整的 | 1. 极致压缩,生成的 CSS 文件体积更小 2. 语义关联,将被继承的元素在逻辑上关联起来 |
| 缺点 | 1. 代码冗余,可能导致最终的 CSS 文件体积较大 2. 性能 (在 Gzip 压缩下,体积差异通常不显著) | 1. 无法传参,只能继承静态样式块 2. 破坏源顺序,所有被继承的选择器会被提到文件的最前面 3. 级联风险,可能产生意料之外的样式关系 |
| 适用场景 | 绝大多数场景:组件样式、工具类、响应式媒体查询、任何需要动态参数的复用。 | 极少数场景:多个完全不相关的选择器需要共享一个 完全静态 的样式块,且压缩文件体积是首要目标。 |
默认并优先使用
@mixin。在现代前端工程中,Gzip 压缩已经极大地缓解了@mixin带来的代码冗余问题。而@mixin带来的 安全性、可预测性、可传参的灵活性 以及对 组件化思想的贴合,其价值远超@extend在体积上带来的微小优势。仅在特定场景下,谨慎使用
@extend %placeholder。当你明确知道,有多个 毫无关联 的、分散在各处的选择器需要共享一个 完全静态(无需参数)的样式定义,并且你的项目对最终产出的 CSS 大小有着极其严苛的要求时,可以考虑使用@extend配合占位符。永远不要
@extend .class。直接继承一个具体的类,是导致样式表混乱和难以维护的根源。请将这条视为团队的编码红线。
总而言之,将 @mixin 作为你的瑞士军刀,将 @extend %placeholder 视为一把仅在特殊情况下使用的手术刀。








