第一章:SCSS 基础核心语法

第一章:SCSS 基础核心语法

摘要: 欢迎来到 SCSS 的世界!在本章中,我们将正式从原生 CSS 迈向更强大、更具工程化能力的 CSS 预处理器。您将学习 SCSS 的核心语法,包括 变量 的声明与使用、嵌套 规则如何简化代码结构,以及最重要的——如何通过现代 模块化 方案(@use@forward)来组织一个清晰、可扩展、无污染的 SCSS 项目。本章还将涵盖编译环境的搭建与核心 调试技巧,为您后续的学习扫清一切障碍。


在本章的学习地图中,我们将:

  1. 首先,明确 SCSS 语法搭建编译环境,掌握将 .scss 文件“翻译”成浏览器认识的 .css 文件的方法,并学会 Source Maps 调试技巧。
  2. 接着,学习 变量 ($),告别在代码中重复书写“魔术数字”的时代。
  3. 然后,我们将深入 嵌套 (Nesting),用更直观的方式编写具有清晰层级关系的样式。
  4. 最后,我们将学习 SCSS 最重要的工程化特性——模块化,并搭建一个科学的、可扩展的 SCSS 项目结构。

1.1. 语法与编译环境

1.1.1. SCSS vs. Sass:理解两种语法

正如序章所述,我们选择学习的 SCSS (.scss 文件) 语法是 CSS 的超集。这意味着任何有效的 CSS 代码本身就是有效的 SCSS 代码,您可以在 .scss 文件中无缝地编写和粘贴任何原生 CSS。这是它学习曲线平缓的关键。

1.1.2. 搭建编译环境

浏览器不直接认识 SCSS,它只认识 CSS。因此,我们需要一个“翻译官”——编译器,将我们的 .scss 文件转换为 .css 文件。在 2025 年,我们几乎总是在一个现代前端项目(如使用 Vite 或 Webpack)中开发,这些工具已经内置了 SCSS 的编译能力,通常我们只需安装一个依赖即可。

独立使用: 如果您想独立编译,官方 唯一推荐 的实现是 Dart Sass

  1. 安装 (需要 Node.js 环境):
    1
    npm install -g sass
  2. 编译:
    1
    2
    3
    4
    5
    # 将 input.scss 编译成 output.css
    sass input.scss output.css

    # 开启“监视”模式,每当 input.scss 文件保存时,自动重新编译
    sass --watch input.scss output.css

1.1.3. 调试技巧:精通 Source Maps

痛点背景: 当你在浏览器开发者工具中审查一个元素时,你看到的样式规则指向的是编译后的 .css 文件(例如 output.css 的第 158 行),但你真正需要修改的是 .scss 源文件(例如 _card.scss 的第 12 行)。在复杂的项目中,这种“源码映射”的缺失会极大地拖慢调试效率。

解决方案: Source Maps (.css.map 文件) 就是为了解决这个问题而生的。它像一张地图,记录了编译后的 CSS 代码与 SCSS 源码之间的精确位置对应关系。

如何开启:

  • 构建工具: Vite、Webpack 等工具在开发模式下 (development mode) 默认开启 Source Maps。
  • 命令行:
    1
    2
    # Dart Sass 默认就会生成 Source Map
    sass --watch input.scss output.css

掌握 Source Maps 是 SCSS 开发的必备技巧。它能让你的开发体验如丝般顺滑,仿佛浏览器能直接运行 SCSS 一样。


1.2. 变量 ($) 与数据类型

1.2.1. 变量的声明、作用域与 !default

与原生 CSS 变量 (--var) 不同,SCSS 变量以 $ 符号开头,它在 编译时 就会被替换为静态的值。

  • 声明与使用:
    1
    2
    3
    4
    5
    6
    7
    $primary-color: #3498db;
    $base-spacing: 16px;

    .button {
    background-color: $primary-color;
    padding: $base-spacing / 2 $base-spacing;
    }
  • 作用域: SCSS 变量存在块级作用域。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $global-color: black;

    .container {
    $local-color: white; /* 仅在 .container 内部有效 */
    background-color: $global-color;
    color: $local-color;
    }

    .footer {
    // 错误!无法访问 .container 内部的 $local-color
    // color: $local-color;
    }
  • !default: 这个标志用于设置变量的 默认值。如果该变量已经被赋值,那么 !default 这一行将被忽略;如果变量尚未赋值,则使用该默认值。这在编写可配置的组件库或框架时非常有用。
    1
    2
    3
    4
    5
    6
    7
    // _library.scss
    $button-bg: #3498db !default; // 库提供一个默认颜色
    .button { background-color: $button-bg; }

    // my-project.scss
    $button-bg: #e74c3c; // 在导入库之前,覆盖掉默认值
    @use 'library'; // 此时按钮的背景色将是红色

1.2.2. SCSS 数据类型简介

SCSS 支持多种数据类型,为编写逻辑和函数提供了基础。

数据类型示例描述
Number16px, 1.5, 10%带或不带单位的数字。
String"Hello", 'SCSS', bold带引号或不带引号的文本。
Color#fff, rgba(0,0,0,0.5)颜色值。
Booleantrue, false布尔值。
List1px 2px, (Helvetica, Arial)用空格或逗号分隔的一组值。
Map(key1: val1, key2: val2)键值对集合,类似 JSON 对象。
nullnull表示空值。

1.3. 嵌套 (Nesting)

嵌套是 SCSS 最为人熟知的功能之一,它允许我们将层级选择器写得更像 HTML 结构,极大地提高了代码的可读性和组织性。

1.3.1. 选择器嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 传统 CSS 写法 */
nav { background: #f4f4f4; }
nav ul { list-style: none; }
nav ul li { display: inline-block; }
nav ul li a { color: blue; }

/* SCSS 嵌套写法 */
nav {
background: #f4f4f4;

ul {
list-style: none;

li {
display: inline-block;

a {
color: blue;
}
}
}
}

1.3.2. 父选择器引用 (&) 的妙用

& 符号是一个特殊的选择器,它代表了 当前的父选择器

  • 用于伪类: 这是最常见的用法。
    1
    2
    3
    4
    5
    6
    7
    a {
    color: blue;
    &:hover {
    color: red;
    }
    }
    // 编译为: a { color: blue; } a:hover { color: red; }
  • 用于拼接 BEM 类名: & 可以像字符串一样,与其他文本拼接,是实践 BEM 命名规范的神器。

1.3.3. 实战:使用嵌套和 & 快速构建 BEM 风格的卡片样式

任务: 使用 BEM 规范 (.card, .card__title, .card--primary) 来定义一个卡片组件,但用 SCSS 的嵌套和 & 功能来编写样式,保持代码的结构化。

1
2
3
4
. 📂 scss
├── 📄 index.css
├── 📄 index.css.map
└── 📄 index.scss

文件路径: scss\index.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
.card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 20px;
width: 250px;
font-family: sans-serif;

&__title {
margin-top: 0;
}

&__content {
margin-bottom: 15px;
}

&__button {
padding: 8px 12px;
border: none;
cursor: pointer;
}

/* 使用 & 拼接 Modifier */
&--primary {
border-color: #3498db;

/* 在 Modifier 内部,可以继续嵌套,影响其内部的 Element */
.card__title {
color: #3498db;
}

.card__button {
background-color: #3498db;
color: white;
}
}

&--secondary {
border-color: #e74c3c;

.card__title {
color: #e74c3c;
}

.card__button {
background-color: #e74c3c;
color: white;
}
}
}

文件路径: index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scss卡片实战</title>
<!-- 引入编译好的css -->
<link rel="stylesheet" href="scss/index.css">
</head>
<!-- 这里可以给他改成secondary查看效果 -->
<div class="card card--primary">
<h3 class="card__title">SCSS 嵌套实战</h3>
<p class="card__content">
这段 SCSS 代码通过嵌套和 & 符号,优雅地生成了 BEM 风格的 CSS。
</p>
<button class="card__button">阅读更多</button>
</div>
</body>

</html>

1.4. 模块化 (@use@forward):告别 @import

1.4.1. 痛点解析:@import 的全局污染问题

在旧的 Sass 语法中,@import 规则是组织样式的主要方式。但它的工作原理非常简单粗暴:将所有被导入文件的内容“复制粘贴”到同一个地方,最终编译成一个巨大的 CSS 文件。

这种机制导致了一个核心问题:全局作用域。所有文件中定义的变量、混合宏(mixin)和函数都存在于同一个全局命名空间下。这极易导致命名冲突,尤其是在大型项目中,你很可能在不知不觉中覆盖了另一个文件中的同名变量,从而引发难以追踪和调试的 bug。

1.4.2. @use:现代模块化核心

为了彻底解决 @import 的弊端,Dart Sass 引入了全新的 @use 规则。它借鉴了现代编程语言(如 JavaScript 的 import)的模块化思想,为 Sass 带来了真正的模块系统。

  • 默认命名空间: 当你 @use 'variables'; 时,_variables.scss 文件中的所有公开成员(变量、mixin 等)都会被加载到一个以文件名命名的命名空间中。你必须通过 variables. 前缀来访问它们,例如 variables.$primary-color。这从根本上杜绝了命名冲突。

  • 私有成员: 在模块文件中,任何以 -_ 开头的变量、mixin 或函数,都将被视为私有成员。这意味着它们只能在当前文件内部使用,无法被外部通过 @use 访问,从而实现了更好的封装。

  • 自定义别名: 如果模块名太长,你可以为它设置一个更短的别名,提高代码可读性。例如:@use 'variables' as vars;,之后便可以通过 vars.$primary-color 来访问。

1.4.3. @forward:构建样式库的瑞士军刀

当项目结构变得复杂时,你可能不希望使用者 @use 多个底层模块文件。@forward 规则就是为此而生。它允许一个文件(通常是文件夹的入口文件,如 _index.scss)加载其他模块的成员,并将它们“转发”出去,使其对外部可见。

想象一下,abstracts 文件夹下有 _variables.scss_mixins.scss。我们可以创建一个 abstracts/_index.scss 文件:

1
2
3
// scss/abstracts/_index.scss
@forward 'variables';
@forward 'mixins';

这样,其他文件只需要 @use 'abstracts'; (Sass 会自动寻找 _index.scss),就可以通过 abstracts.$primary-color@include abstracts.flex-center 同时访问到这两个模块的成员了,大大简化了模块的引用。

1.4.4. 实战:搭建一个科学的 SCSS 项目文件结构

现在,让我们结合所学,搭建一个业界流行的 7-1 Pattern 简化版结构,并学习如何正确组织和导入我们的模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
scss/
├── abstracts/ # 存放变量、函数、mixin 等抽象内容
│ ├── _variables.scss
│ └── _mixins.scss

├── base/ # 存放全局基础样式
│ ├── _typography.scss
│ └── _base.scss

├── components/ # 存放组件样式
│ ├── _button.scss
│ └── _card.scss

└── main.scss # 主入口文件

关于样式重置(Reset)

base/ 目录中,通常会有一个 _reset.scss 文件来统一不同浏览器的默认样式。手动编写和维护一个全面的 Reset 文件非常繁琐。因此,我们强烈推荐使用一个成熟的第三方库来完成这项工作。

推荐库:scss-reset

scss-reset 是一个优秀的现代化 CSS 重置库,它融合了 Meyer Reset、modern-css-reset 和 normalize.css 的优点,开箱即用。

  1. 安装它 (需要 Node.js 环境):

    1
    npm i scss-reset --save-dev
  2. 在主入口文件中使用它:

现在,我们来编写项目的主入口文件 scss/main.scss。它的职责就是作为项目“司令塔”,按正确的顺序编排和导入所有模块。

文件路径: scss/main.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 导入第三方库和全局配置
// 优先处理样式重置,确保后续样式基于一个干净、统一的基础
@use '../node_modules/scss-reset'; // 使用相对路径导入 scss-reset
@use 'abstracts/variables' as vars; // 使用别名 'vars' 方便调用
@use 'abstracts/mixins' as mix;

// 2. 导入基础样式
@use 'base/typography';
@use 'base/base';

// 3. 导入组件样式
@use 'components/button';
@use 'components/card';

如何使用模块?

现在,当我们在组件文件中编写样式时,就可以通过命名空间来安全地使用变量和 mixin 了。

示例: scss/components/_button.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 在 _button.scss 中,我们需要再次 @use 依赖的模块
// Sass 非常智能,即使多处 @use 同一个文件,它也只会被解析和执行一次
@use '../abstracts/variables' as vars;
@use '../abstracts/mixins' as mix;

.btn {
// 使用来自 _variables.scss 的变量
background-color: vars.$primary-color;
color: white;
padding: 10px 20px;
border-radius: vars.$border-radius-default;

// 使用来自 _mixins.scss 的 mixin
@include mix.flex-center; // 假设有一个名为 flex-center 的 mixin

&:hover {
background-color: vars.$primary-color-dark;
}
}

这种基于 @use 和清晰文件结构的模块化工作流,清晰地分离了不同类型的样式,确保了模块之间的独立性和无冲突,是所有专业 SCSS 项目的基石。