CSS 预处理器 - Sass

章节介绍

Sass(Syntactically Awesome Style Sheets)是目前最流行的 CSS 预处理器之一,它扩展了 CSS 的功能,提供了许多高级特性,如变量、混合、嵌套、继承等。这些特性可以大大提高 CSS 代码的可维护性、可重用性和开发效率。本章节将详细介绍 Sass 的核心特性和使用方法,帮助你掌握这个强大的 CSS 开发工具。

核心知识点讲解

1. Sass 的安装和使用

安装 Sass

Sass 可以通过多种方式安装,最常用的是通过 npm 安装:

# 全局安装
npm install -g sass

# 项目本地安装
npm install --save-dev sass

基本使用

Sass 文件有两种扩展名:.scss.sass.scss 是 Sass 的新语法,兼容 CSS 语法;.sass 是旧语法,使用缩进代替大括号和分号。

# 编译单个文件
sass input.scss output.css

# 监视文件变化并自动编译
sass --watch input.scss:output.css

# 监视整个目录
sass --watch src/scss:dist/css

2. 变量

变量是 Sass 最基本的特性之一,它允许你存储和重用值。

变量定义和使用

// 定义变量
$primary-color: #3498db;
$font-size: 16px;
$spacing: 10px;

// 使用变量
body {
  font-size: $font-size;
  color: $primary-color;
}

.container {
  margin: $spacing;
  padding: $spacing;
}

变量作用域

Sass 变量有全局作用域和局部作用域:

// 全局变量
$color: red;

.container {
  // 局部变量,只在 .container 内部有效
  $color: blue;
  color: $color; // 输出 blue
}

.text {
  color: $color; // 输出 red
}

变量插值

变量插值允许你在选择器、属性名和字符串中使用变量:

$prefix: btn;

.#{$prefix} {
  display: inline-block;
  padding: 10px 20px;
}

.#{$prefix}-primary {
  background-color: #3498db;
  color: white;
}

3. 嵌套

嵌套是 Sass 的另一个核心特性,它允许你在选择器内部嵌套其他选择器,反映 HTML 的层次结构。

基本嵌套

nav {
  ul {
    list-style: none;
    margin: 0;
    padding: 0;
  }
  
  li {
    display: inline-block;
    margin-right: 10px;
  }
  
  a {
    text-decoration: none;
    color: #333;
    
    &:hover {
      color: #3498db;
    }
  }
}

父选择器引用

使用 & 可以引用父选择器:

.btn {
  display: inline-block;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  
  &:hover {
    opacity: 0.8;
  }
  
  &:active {
    transform: translateY(1px);
  }
  
  &-primary {
    background-color: #3498db;
    color: white;
  }
  
  &-secondary {
    background-color: #2ecc71;
    color: white;
  }
}

4. 混合(Mixins)

混合是 Sass 中最强大的特性之一,它允许你创建可重用的样式块。

基本混合

// 定义混合
@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

// 使用混合
.container {
  @include flex-center;
  height: 400px;
}

.card {
  @include flex-center;
  flex-direction: column;
}

带参数的混合

// 定义带参数的混合
@mixin button($bg-color, $text-color: white, $padding: 10px 20px) {
  display: inline-block;
  padding: $padding;
  background-color: $bg-color;
  color: $text-color;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  &:hover {
    opacity: 0.8;
  }
}

// 使用带参数的混合
.btn-primary {
  @include button(#3498db);
}

.btn-secondary {
  @include button(#2ecc71, white, 8px 16px);
}

带默认值的混合

// 定义带默认值的混合
@mixin box-shadow($shadow: 0 2px 4px rgba(0, 0, 0, 0.1)) {
  -webkit-box-shadow: $shadow;
  -moz-box-shadow: $shadow;
  box-shadow: $shadow;
}

// 使用带默认值的混合
.card {
  @include box-shadow;
}

.card-hover {
  @include box-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
}

5. 继承

继承允许你从一个选择器继承样式到另一个选择器,减少代码冗余。

基本继承

// 基础样式
.btn {
  display: inline-block;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

// 继承基础样式
.btn-primary {
  @extend .btn;
  background-color: #3498db;
  color: white;
}

.btn-secondary {
  @extend .btn;
  background-color: #2ecc71;
  color: white;
}

占位符选择器

占位符选择器(以 % 开头)是一种特殊的选择器,它本身不会被编译到 CSS 中,只有被继承时才会生效:

// 占位符选择器
%btn {
  display: inline-block;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

// 继承占位符选择器
.btn-primary {
  @extend %btn;
  background-color: #3498db;
  color: white;
}

.btn-secondary {
  @extend %btn;
  background-color: #2ecc71;
  color: white;
}

6. 操作符

Sass 提供了各种操作符,如算术操作符、比较操作符、逻辑操作符等,可以在样式中进行计算。

算术操作符

$base-font-size: 16px;
$container-width: 960px;
$column-count: 3;

body {
  font-size: $base-font-size;
}

.container {
  width: $container-width;
  margin: 0 auto;
}

.column {
  width: $container-width / $column-count;
  float: left;
  margin-right: 20px;
  
  &:last-child {
    margin-right: 0;
  }
}

.text-lg {
  font-size: $base-font-size * 1.5;
}

.text-sm {
  font-size: $base-font-size * 0.8;
}

颜色操作

$primary-color: #3498db;

.btn {
  background-color: $primary-color;
  
  &:hover {
    // 使颜色变暗 10%
    background-color: darken($primary-color, 10%);
  }
  
  &:active {
    // 使颜色变亮 5%
    background-color: lighten($primary-color, 5%);
  }
}

// 其他颜色函数
$color: #3498db;

// 调整饱和度
saturate($color, 10%);

// 降低饱和度
desaturate($color, 10%);

// 调整透明度
transparentize($color, 0.5);

// 混合颜色
mix($color, #fff, 50%);

7. 函数

Sass 提供了许多内置函数,同时也允许你定义自己的函数。

内置函数

// 字符串函数
$string: "Hello, Sass!";

// 转换为大写
uppercase($string);

// 转换为小写
lowercase($string);

// 首字母大写
capitalize($string);

// 数字函数
$number: 10.5;

// 取整
round($number);

// 向上取整
ceil($number);

// 向下取整
floor($number);

// 列表函数
$list: 1, 2, 3, 4, 5;

// 获取列表长度
length($list);

// 获取列表中的元素
nth($list, 2);

// 映射函数
$map: (
  primary: #3498db,
  secondary: #2ecc71,
  danger: #e74c3c
);

// 获取映射中的值
map-get($map, primary);

// 检查映射中是否存在键
map-has-key($map, primary);

自定义函数

// 定义自定义函数
@function calculate-width($container-width, $column-count, $gutter) {
  @return ($container-width - ($gutter * ($column-count - 1))) / $column-count;
}

// 使用自定义函数
.container {
  width: 960px;
  margin: 0 auto;
}

.column {
  width: calculate-width(960px, 3, 20px);
  float: left;
  margin-right: 20px;
  
  &:last-child {
    margin-right: 0;
  }
}

8. 导入

Sass 的导入功能允许你将样式拆分成多个文件,提高代码的可维护性。

导入文件

// 导入变量
@import 'variables';

// 导入混合
@import 'mixins';

// 导入基础样式
@import 'base';

// 导入组件样式
@import 'components/buttons';
@import 'components/cards';
@import 'components/forms';

// 导入布局样式
@import 'layouts/header';
@import 'layouts/footer';
@import 'layouts/grid';

部分文件

Sass 部分文件以 _ 开头,它们不会被单独编译成 CSS 文件,只能被其他文件导入:

scss/
├── _variables.scss
├── _mixins.scss
├── _base.scss
├── components/
│   ├── _buttons.scss
│   ├── _cards.scss
│   └── _forms.scss
├── layouts/
│   ├── _header.scss
│   ├── _footer.scss
│   └── _grid.scss
└── style.scss // 主文件,导入所有部分文件

9. 控制指令

Sass 提供了控制指令,如 @if@for@each@while,允许你在样式中使用逻辑控制。

@if 指令

$theme: dark;

body {
  @if $theme == dark {
    background-color: #333;
    color: white;
  } @else if $theme == light {
    background-color: #fff;
    color: #333;
  } @else {
    background-color: #f5f5f5;
    color: #333;
  }
}

@for 指令

// 从 1 到 4 循环
@for $i from 1 through 4 {
  .col-#{$i} {
    width: 100% / 4 * $i;
  }
}

// 从 1 到 3 循环
@for $i from 1 to 4 {
  .mt-#{$i} {
    margin-top: $i * 10px;
  }
}

@each 指令

// 遍历列表
$colors: primary #3498db, secondary #2ecc71, danger #e74c3c;

@each $name, $color in $colors {
  .btn-#{$name} {
    background-color: $color;
    color: white;
  }
}

// 遍历映射
$spacing: (
  xs: 5px,
  sm: 10px,
  md: 15px,
  lg: 20px,
  xl: 30px
);

@each $name, $value in $spacing {
  .m-#{$name} {
    margin: $value;
  }
  
  .p-#{$name} {
    padding: $value;
  }
}

@while 指令

$i: 1;

@while $i <= 5 {
  .text-#{$i} {
    font-size: 12px + $i * 2;
  }
  
  $i: $i + 1;
}

实用案例分析

案例一:使用 Sass 变量管理颜色方案

场景描述

需要管理一个网站的颜色方案,确保颜色的一致性和可维护性。

实现代码

// _variables.scss
// 颜色变量
$colors: (
  primary: #3498db,
  secondary: #2ecc71,
  danger: #e74c3c,
  warning: #f39c12,
  success: #27ae60,
  dark: #333,
  light: #f5f5f5,
  gray: #999,
  white: #fff
);

// 从映射中获取颜色值
@function color($key) {
  @return map-get($colors, $key);
}

// _mixins.scss
// 按钮混合
@mixin button($color-key, $padding: 10px 20px) {
  display: inline-block;
  padding: $padding;
  background-color: color($color-key);
  color: color(white);
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  &:hover {
    opacity: 0.8;
  }
}

// _buttons.scss
// 导入变量和混合
@import 'variables';
@import 'mixins';

// 按钮样式
.btn {
  display: inline-block;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  &:hover {
    opacity: 0.8;
  }
}

.btn-primary {
  @include button(primary);
}

.btn-secondary {
  @include button(secondary);
}

.btn-danger {
  @include button(danger);
}

代码解析

  1. 颜色变量管理:使用映射存储所有颜色值,便于统一管理
  2. 颜色函数:创建 color() 函数,方便从映射中获取颜色值
  3. 按钮混合:创建带参数的混合,根据颜色键生成不同样式的按钮
  4. 按钮样式:使用混合生成不同类型的按钮样式

案例二:使用 Sass 创建响应式网格系统

场景描述

需要创建一个响应式网格系统,支持不同屏幕尺寸下的布局调整。

实现代码

// _variables.scss
// 网格变量
$grid-columns: 12;
$grid-gutter: 20px;

// 断点变量
$breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

// 从映射中获取断点值
@function breakpoint($key) {
  @return map-get($breakpoints, $key);
}

// _mixins.scss
// 响应式混合
@mixin responsive($breakpoint) {
  @media (min-width: breakpoint($breakpoint)) {
    @content;
  }
}

// 网格列混合
@mixin col($columns, $total-columns: $grid-columns) {
  width: ($columns / $total-columns) * 100%;
}

// _grid.scss
// 导入变量和混合
@import 'variables';
@import 'mixins';

// 容器样式
.container {
  width: 100%;
  padding-left: $grid-gutter / 2;
  padding-right: $grid-gutter / 2;
  margin-left: auto;
  margin-right: auto;
  
  @include responsive(sm) {
    max-width: 540px;
  }
  
  @include responsive(md) {
    max-width: 720px;
  }
  
  @include responsive(lg) {
    max-width: 960px;
  }
  
  @include responsive(xl) {
    max-width: 1140px;
  }
}

// 行样式
.row {
  display: flex;
  flex-wrap: wrap;
  margin-left: -$grid-gutter / 2;
  margin-right: -$grid-gutter / 2;
}

// 列样式
.col {
  position: relative;
  width: 100%;
  padding-left: $grid-gutter / 2;
  padding-right: $grid-gutter / 2;
}

// 生成列类
@for $i from 1 through $grid-columns {
  .col-#{$i} {
    @include col($i);
  }
  
  @include responsive(sm) {
    .col-sm-#{$i} {
      @include col($i);
    }
  }
  
  @include responsive(md) {
    .col-md-#{$i} {
      @include col($i);
    }
  }
  
  @include responsive(lg) {
    .col-lg-#{$i} {
      @include col($i);
    }
  }
  
  @include responsive(xl) {
    .col-xl-#{$i} {
      @include col($i);
    }
  }
}

代码解析

  1. 网格变量:定义网格列数、间距和断点
  2. 响应式混合:创建 responsive() 混合,生成媒体查询
  3. 网格列混合:创建 col() 混合,计算列宽
  4. 容器样式:定义不同断点下的容器最大宽度
  5. 行和列样式:定义行和列的基本样式
  6. 生成列类:使用 @for 指令生成不同断点下的列类

案例三:使用 Sass 管理字体和排版

场景描述

需要管理网站的字体和排版系统,确保字体大小、行高、字重等的一致性。

实现代码

// _variables.scss
// 字体变量
$fonts: (
  primary: 'Arial', sans-serif,
  secondary: 'Georgia', serif,
  monospace: 'Courier New', monospace
);

// 字体大小变量
$font-sizes: (
  xs: 12px,
  sm: 14px,
  base: 16px,
  md: 18px,
  lg: 20px,
  xl: 24px,
  xxl: 30px,
  xxxl: 36px
);

// 行高变量
$line-heights: (
  tight: 1.2,
  normal: 1.5,
  loose: 1.8
);

// 字重变量
$font-weights: (
  light: 300,
  normal: 400,
  medium: 500,
  semibold: 600,
  bold: 700
);

// 从映射中获取值
@function font($key) {
  @return map-get($fonts, $key);
}

@function font-size($key) {
  @return map-get($font-sizes, $key);
}

@function line-height($key) {
  @return map-get($line-heights, $key);
}

@function font-weight($key) {
  @return map-get($font-weights, $key);
}

// _mixins.scss
// 排版混合
@mixin typography($size, $weight: normal, $line-height: normal) {
  font-size: font-size($size);
  font-weight: font-weight($weight);
  line-height: line-height($line-height);
}

// _typography.scss
// 导入变量和混合
@import 'variables';
@import 'mixins';

// 基础排版
body {
  font-family: font(primary);
  @include typography(base);
  color: #333;
}

// 标题样式
h1 {
  @include typography(xxxl, bold, tight);
  margin-bottom: font-size(md);
}

h2 {
  @include typography(xxl, semibold, tight);
  margin-bottom: font-size(sm);
}

h3 {
  @include typography(xl, semibold, tight);
  margin-bottom: font-size(sm);
}

h4 {
  @include typography(lg, medium, normal);
  margin-bottom: font-size(xs);
}

h5 {
  @include typography(md, medium, normal);
  margin-bottom: font-size(xs);
}

h6 {
  @include typography(base, medium, normal);
  margin-bottom: font-size(xs);
}

// 文本样式
.text-xs {
  @include typography(xs);
}

.text-sm {
  @include typography(sm);
}

.text-base {
  @include typography(base);
}

.text-lg {
  @include typography(lg);
}

.text-xl {
  @include typography(xl);
}

// 字重样式
.font-light {
  font-weight: font-weight(light);
}

.font-normal {
  font-weight: font-weight(normal);
}

.font-medium {
  font-weight: font-weight(medium);
}

.font-semibold {
  font-weight: font-weight(semibold);
}

.font-bold {
  font-weight: font-weight(bold);
}

代码解析

  1. 字体变量:使用映射存储字体、字体大小、行高和字重
  2. 字体函数:创建函数从映射中获取字体相关值
  3. 排版混合:创建混合,根据参数设置字体样式
  4. 基础排版:设置 body 的基础字体样式
  5. 标题样式:使用混合设置不同级别标题的样式
  6. 文本样式:创建不同字体大小的文本类
  7. 字重样式:创建不同字重的文本类

Sass 项目结构最佳实践

推荐的项目结构

scss/
├── _variables.scss          // 变量定义
├── _mixins.scss             // 混合定义
├── _functions.scss          // 函数定义
├── _reset.scss              // 重置样式
├── _base.scss               // 基础样式
├── components/              // 组件样式
│   ├── _buttons.scss        // 按钮组件
│   ├── _cards.scss          // 卡片组件
│   ├── _forms.scss          // 表单组件
│   ├── _navigation.scss     // 导航组件
│   └── _modals.scss         // 模态框组件
├── layouts/                 // 布局样式
│   ├── _header.scss         // 头部布局
│   ├── _footer.scss         // 页脚布局
│   ├── _grid.scss           // 网格布局
│   └── _sidebar.scss        // 侧边栏布局
├── pages/                   // 页面特定样式
│   ├── _home.scss           // 首页样式
│   ├── _about.scss          // 关于页样式
│   └── _contact.scss        // 联系页样式
├── utils/                   // 工具类
│   ├── _typography.scss     // 排版工具类
│   ├── _spacing.scss        // 间距工具类
│   ├── _colors.scss         // 颜色工具类
│   └── _flex.scss           // Flexbox 工具类
└── style.scss               // 主文件,导入所有部分文件

导入顺序

  1. 变量、混合和函数:这些是基础,其他样式会依赖它们
  2. 重置和基础样式:重置浏览器默认样式,设置全局基础样式
  3. 组件样式:可重用组件的样式
  4. 布局样式:页面布局相关的样式
  5. 页面特定样式:特定页面的样式
  6. 工具类:通用的工具类

代码示例:完整的 Sass 项目示例

// style.scss
// 导入变量、混合和函数
@import 'variables';
@import 'mixins';
@import 'functions';

// 导入重置和基础样式
@import 'reset';
@import 'base';

// 导入组件样式
@import 'components/buttons';
@import 'components/cards';
@import 'components/forms';
@import 'components/navigation';

// 导入布局样式
@import 'layouts/header';
@import 'layouts/footer';
@import 'layouts/grid';

// 导入页面特定样式
@import 'pages/home';
@import 'pages/about';
@import 'pages/contact';

// 导入工具类
@import 'utils/typography';
@import 'utils/spacing';
@import 'utils/colors';
@import 'utils/flex';

// _variables.scss
// 颜色变量
$colors: (
  primary: #3498db,
  secondary: #2ecc71,
  danger: #e74c3c,
  warning: #f39c12,
  success: #27ae60,
  dark: #333,
  light: #f5f5f5,
  gray: #999,
  white: #fff
);

// 字体变量
$fonts: (
  primary: 'Arial', sans-serif,
  secondary: 'Georgia', serif
);

// 字体大小变量
$font-sizes: (
  xs: 12px,
  sm: 14px,
  base: 16px,
  md: 18px,
  lg: 20px,
  xl: 24px
);

// 间距变量
$spacing: (
  xs: 5px,
  sm: 10px,
  md: 15px,
  lg: 20px,
  xl: 30px,
  xxl: 40px
);

// _mixins.scss
// 按钮混合
@mixin button($bg-color, $text-color: white, $padding: 10px 20px) {
  display: inline-block;
  padding: $padding;
  background-color: $bg-color;
  color: $text-color;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  &:hover {
    opacity: 0.8;
  }
}

// 响应式混合
@mixin responsive($breakpoint) {
  @media (min-width: $breakpoint) {
    @content;
  }
}

// _base.scss
// 基础样式
body {
  font-family: map-get($fonts, primary);
  font-size: map-get($font-sizes, base);
  line-height: 1.5;
  color: map-get($colors, dark);
  background-color: map-get($colors, light);
}

// 链接样式
a {
  color: map-get($colors, primary);
  text-decoration: none;
  
  &:hover {
    text-decoration: underline;
  }
}

// 标题样式
h1, h2, h3, h4, h5, h6 {
  margin-top: 0;
  margin-bottom: map-get($spacing, md);
  font-weight: bold;
}

h1 {
  font-size: map-get($font-sizes, xl);
}

h2 {
  font-size: map-get($font-sizes, lg);
}

h3 {
  font-size: map-get($font-sizes, md);
}

// _components/buttons.scss
// 按钮样式
.btn {
  display: inline-block;
  padding: map-get($spacing, sm) map-get($spacing, lg);
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: map-get($font-sizes, base);
  font-weight: bold;
  text-align: center;
  text-decoration: none;
  
  &:hover {
    opacity: 0.8;
  }
  
  &:active {
    transform: translateY(1px);
  }
}

.btn-primary {
  @include button(map-get($colors, primary));
}

.btn-secondary {
  @include button(map-get($colors, secondary));
}

.btn-danger {
  @include button(map-get($colors, danger));
}

// _utils/spacing.scss
// 间距工具类
@each $name, $value in $spacing {
  .m-#{$name} {
    margin: $value;
  }
  
  .mt-#{$name} {
    margin-top: $value;
  }
  
  .mb-#{$name} {
    margin-bottom: $value;
  }
  
  .ml-#{$name} {
    margin-left: $value;
  }
  
  .mr-#{$name} {
    margin-right: $value;
  }
  
  .p-#{$name} {
    padding: $value;
  }
  
  .pt-#{$name} {
    padding-top: $value;
  }
  
  .pb-#{$name} {
    padding-bottom: $value;
  }
  
  .pl-#{$name} {
    padding-left: $value;
  }
  
  .pr-#{$name} {
    padding-right: $value;
  }
}

章节总结

本章节详细介绍了 Sass 预处理器的核心特性和使用方法,包括:

  • 变量:存储和重用值,提高代码的可维护性
  • 嵌套:反映 HTML 的层次结构,减少代码冗余
  • 混合:创建可重用的样式块,支持参数和默认值
  • 继承:从一个选择器继承样式到另一个选择器
  • 操作符:在样式中进行计算,支持算术和颜色操作
  • 函数:使用内置函数和自定义函数处理值
  • 导入:将样式拆分成多个文件,提高代码的组织性
  • 控制指令:使用逻辑控制指令生成样式
  • 项目结构:推荐的 Sass 项目结构和导入顺序

通过本章节的学习,你应该能够:

  1. 安装和使用 Sass 预处理器
  2. 掌握 Sass 的核心特性,如变量、嵌套、混合、继承等
  3. 使用 Sass 创建可维护、可重用的样式代码
  4. 设计合理的 Sass 项目结构
  5. 应用 Sass 最佳实践,提高 CSS 开发效率

Sass 是一个强大的 CSS 开发工具,它可以大大提高你的开发效率和代码质量。通过合理使用 Sass 的特性,你可以编写更加简洁、可维护、可扩展的 CSS 代码,为项目的成功做出贡献。

« 上一篇 CSS3 代码规范 - 编写高质量、可维护的 CSS 代码 下一篇 » CSS 预处理器 - Less - 简洁高效的 CSS 扩展语言