学习:Sass

Sass安装

复制代码
npm install -g sass

使用介绍

创建demo.scss

css 复制代码
// Sass 变量定义
$primary-color: #007bff;
$secondary-color: #6c757d;
$bg-color: rgb(218, 245, 243);
$text-color: #333;
$border-radius: 8px;

// 基础样式
body {
  background-color: $bg-color;
  color: $text-color;
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 20px;
}

// 容器
.container {
  max-width: 800px;
  margin: 0 auto;
}

// 标题
h1 {
  color: $primary-color;
  text-align: center;
}

h2 {
  color: $primary-color;
}

p {
  line-height: 1.6;
}
// 按钮样式
.btn {
  padding: 10px 20px;
  border: none;
  border-radius: $border-radius;
  margin: 5px;
  cursor: pointer;
  transition: all 0.3s ease;
  background-color: $primary-color;
}

创建demo.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, initial-scale=1.0">
    <title>Sass 简单案例</title>
    <link rel="stylesheet"
        href="demo.css">
</head>

<body>
    <div class="container">
        <h1>h1</h1>
        <h2>h2</h2>
        <p>p</p>
        <button class="btn ">按钮</button>
    </div>
</body>

</html>

将SCSS文件编译为CSS文件

bash 复制代码
sass demo.scss demo.css

demo.scss 是要转换的 SCSS 文件路径,demo.css 是生成的 CSS 文件路径。

生成

CSS.map 文件的作用

.css.map 文件是 Source Map(源映射) 文件,它建立了编译后的CSS代码与原始SCSS源代码之间的映射关系。

为什么需要Source Map?

  1. 调试便利:当你在浏览器开发者工具中检查元素时,可以直接看到原始的SCSS代码行号,而不是编译后的CSS代码
  2. 错误定位:如果CSS有问题,可以快速追溯到SCSS源文件中的具体位置
  3. 开发体验:让预处理器开发体验接近原生CSS开发
bash 复制代码
# 默认会生成Source Map
sass demo.scss demo.css

# 禁用Source Map生成
sass --no-source-map demo.scss demo.css

# 明确启用Source Map(默认行为)
sass --source-map demo.scss demo.css

展示

Sass变量概述

Sass变量允许您存储信息,然后在整个样式表中重复使用。这使得CSS更加可维护、可重用和易于管理。

变量的语法

css 复制代码
// 使用$符号声明变量
$bg-color: rgb(218, 245, 243);;

// 使用变量
body {
  background-color: $bg-color;
}

变量的命名规则

  1. 必须以美元符号( $ )开头
  2. 可以包含字母、数字、下划线(_)和连字符(-)
  3. 不能以数字开头
  4. 区分大小写
css 复制代码
$primary-color: #3498db;      // 有效
$secondary_color: #2ecc71;    // 有效
$fontSize: 16px;              // 有效
$1st-color: red;              // 无效,不能以数字开头

变量的数据类型

Sass支持多种数据类型的变量:

1. 数字

css 复制代码
$base-font-size: 16px;
$line-height: 7;
$border-radius: 4px;

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

2. 字符串

css 复制代码
$font-family: "Helvetica Neue", Arial, sans-serif;
$message: "Hello, Sass!";

h1 {
  font-family: $font-family;
}

3. 颜色

css 复制代码
$primary-color: #3498db;
$secondary-color: #2ecc71;
$transparent-color: rgba(255, 255, 255, 0.8);

.button {
  background-color: $primary-color;
  border-color: $secondary-color;
}

4. 布尔值

css 复制代码
$debug: true;
$rounded-corners: false;

@if $debug {
  border: 1px solid red;
}

@if $rounded-corners {
  border-radius: 5px;
}

5. 列表

css 复制代码
$margin-list: 10px 15px 20px 25px;
$font-stack: "Helvetica", "Arial", sans-serif;
$sizes: small, medium, large;

// 使用nth()函数访问列表元素
.element {
  margin-top: nth($margin-list, 1); // 10px
  font-family: nth($font-stack, 2); // "Arial"
}

6. Maps(键值对)

css 复制代码
$colors: (
  "primary": #3498db,
  "secondary": #2ecc71,
  "accent": #e74c3c
);

.button-primary {
  background-color: map-get($colors, "primary");
}

.button-secondary {
  background-color: map-get($colors, "secondary");
}

7. null

css 复制代码
$variable: null;

// 当变量为null时,它不会被编译到CSS中
.element {
  content: $variable; // 这行不会出现在编译后的CSS中
}

变量的作用域

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

css 复制代码
$global-color: blue; // 全局变量

.container {
  $local-color: red; // 局部变量
  color: $global-color; // 可以访问全局变量
  
  .child {
    color: $local-color; // 可以访问父级的局部变量
  }
}

// 这里无法访问 $local-color,因为它在 .container 块内定义
css 复制代码
<body>
    <text>123水水水水水水水水</text>
    <div class="container">
        <h1>h1</h1>
        <h2>h2</h2>
        <div class="child"
            <p>p</p>
        </div>
    </div>
</body>

使用 !global 标志

css 复制代码
.container {
  $local-color: red !global; // 将局部变量提升为全局变量
}

.footer {
  color: $local-color; // 现在可以访问
}
css 复制代码
    <div class="container">
    </div>
    <div class="footer">
        <text>123</text>
    </div>

使用 !default 标志

!default 标志允许您设置默认值,只有在变量尚未定义时才赋值:

css 复制代码
$primary-color: #3498db !default;

// 如果 $primary-color 已经定义,上面的赋值不会发生
$primary-color: #e74c3c; // 这会覆盖默认值

.button {
  background-color: $primary-color; // 使用 #e74c3c
}

变量的插值

您可以使用 #{} 插值语法在属性名、选择器等地方使用变量:

css 复制代码
$prefix: "user";

.#{$prefix}-header {
  font-size: 20px;
}

.#{$prefix}-content {
  font-size: 16px;
}

// 属性名中使用变量
$property: margin;

.element {
  #{$property}-top: 10px;
  #{$property}-bottom: 20px;
}

变量中的特殊函数

Sass提供了一些内置函数来处理变量:

颜色函数

css 复制代码
$primary-color: #3498db;

.button {
  background-color: $primary-color;
  
  // 颜色变暗
  &:hover {
    background-color: darken($primary-color, 10%);
  }
  
  // 颜色变亮
  &:active {
    background-color: lighten($primary-color, 10%);
  }
  
  // 调整色相
  &.alt {
    background-color: adjust-hue($primary-color, 180deg);
  }
}

数字函数

css 复制代码
$base-size: 16px;

.element {
  // 单位转换
  width: unit($base-size); // 16
  height: unitless($base-size); // false
  
  // 绝对值
  padding: abs(-10px); // 10px
  
  // 四舍五入
  margin: round(15.7px); // 16px
  
  // 最大值/最小值
  font-size: max(14px, $base-size); // 16px
}

字符串函数

css 复制代码
$message: "Hello, Sass!";

.element {
  // 字符串长度
  width: str-length($message); // 12
  
  // 字符串插入
  content: str-insert($message, "World", 7); // "Hello, World!"
  
  // 转换为大写
  content: to-upper-case($message); // "HELLO, SASS!"
}

变量覆盖

变量可以被重新赋值,后面的值会覆盖前面的值:

css 复制代码
$color: blue;
$color: red; // 这会覆盖前面的值

.text {
  color: $color; // 红色
}

实际应用示例

1. 主题系统

css 复制代码
// 定义主题变量
$themes: (
  "light": (
    "bg-color": #ffffff,
    "text-color": #333333,
    "primary": #3498db
  ),
  "dark": (
    "bg-color": #222222,
    "text-color": #eeeeee,
    "primary": #3498db
  )
);

// 创建主题混合
@mixin theme($theme-name) {
  $theme: map-get($themes, $theme-name);
  
  background-color: map-get($theme, "bg-color");
  color: map-get($theme, "text-color");
  
  .btn-primary {
    background-color: map-get($theme, "primary");
  }
}

// 应用主题
.light-theme {
  @include theme("light");
}

.dark-theme {
  @include theme("dark");
}

2. 响应式断点系统

css 复制代码
// 断点变量
$breakpoints: (
  "sm": 576px,
  "md": 768px,
  "lg": 992px,
  "xl": 1200px
);

// 响应式混合
@mixin respond-to($breakpoint) {
  @media (min-width: map-get($breakpoints, $breakpoint)) {
    @content;
  }
}

// 使用
.container {
  width: 100%;
  
  @include respond-to("sm") {
    width: 540px;
  }
  
  @include respond-to("md") {
    width: 720px;
  }
  
  @include respond-to("lg") {
    width: 960px;
  }
}

3. 间距系统

css 复制代码
// 间距变量
$spacers: (
  0: 0,
  1: 0.25rem,
  2: 0.5rem,
  3: 1rem,
  4: 1.5rem,
  5: 3rem
);

// 生成间距类
@each $key, $value in $spacers {
  .m-#{$key} { margin: $value !important; }
  .p-#{$key} { padding: $value !important; }
  
  .mt-#{$key} { margin-top: $value !important; }
  .mb-#{$key} { margin-bottom: $value !important; }
  
  .pt-#{$key} { padding-top: $value !important; }
  .pb-#{$key} { padding-bottom: $value !important; }
}

最佳实践

  1. 有意义的命名 :使用描述性的变量名,如 $primary-color 而不是 $c1
  2. 组织变量 :将相关变量组织在一起,通常放在单独的文件中(如 _variables.scss
  3. 使用默认值 :使用 !default 标志创建可覆盖的变量系统
  4. 合理使用作用域:避免全局污染,适当使用局部变量
  5. 文档化变量:为复杂变量添加注释说明

CSS自定义变量 vs Sass变量

Sass变量和CSS自定义变量(CSS变量)有一些区别:

特性 Sass变量 CSS自定义变量
语法 $variable --variable
使用 property: $variable property: var(--variable)
编译时处理
运行时修改
作用域 Sass作用域规则 DOM作用域
浏览器支持 编译后支持 现代浏览器支持

总结

Sass变量是CSS预处理器的核心功能之一,它们提供了一种强大而灵活的方式来管理样式表中的值。通过合理使用变量,您可以:

  • 提高代码的可维护性
  • 减少重复
  • 创建一致的设计系统
  • 轻松实现主题切换
  • 建立响应式断点系统

变量是掌握Sass的第一步,也是最重要的一步。

Sass 嵌套规则与属性

Sass允许您按照HTML文档的结构嵌套CSS选择器,这使代码更加清晰、有组织性,并且更易于维护。它模仿了HTML的层级结构,让您能够直观地看到元素之间的关系。

传统CSS vs Sass嵌套

传统CSS写法:

css 复制代码
nav ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

nav li {
  display: inline-block;
}

nav a {
  display: block;
  padding: 6px 12px;
  text-decoration: none;
}

Sass嵌套写法:

css 复制代码
nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  
  li {
    display: inline-block;
  }
  
  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

嵌套选择器

1. 后代选择器嵌套

这是最常见的嵌套类型,嵌套的选择器会被编译为后代选择器:

css 复制代码
.container {
  background-color: #f8f9fa;
  
  .header {
    font-size: 24px;
    color: #333;
    
    .title {
      font-weight: bold;
    }
  }
}

// 编译为:
.container {
  background-color: #f8f9fa;
}
.container .header {
  font-size: 24px;
  color: #333;
}
.container .header .title {
  font-weight: bold;
}

2. 子选择器嵌套

使用 > 选择器表示直接子元素:

css 复制代码
nav > ul {
  margin: 0;
  
  > li {
    list-style: none;
  }
}

// 编译为:
nav > ul {
  margin: 0;
}
nav > ul > li {
  list-style: none;
}

3. 相邻兄弟选择器嵌套

使用 + 选择器表示相邻兄弟元素:

css 复制代码
h1 + p {
  font-style: italic;
}

// 编译为:
h1 + p {
  font-style: italic;
}

4. 通用兄弟选择器嵌套

使用 ~ 选择器表示通用兄弟元素:

css 复制代码
h2 ~ p {
  margin-top: 10px;
}

// 编译为:
h2 ~ p {
  margin-top: 10px;
}

父选择器引用(&符号)

& 符号是Sass中最强大的嵌套功能之一,它引用父选择器。

1. 基本用法

css 复制代码
.button {
  display: inline-block;
  padding: 10px 20px;
  
  &:hover {
    background-color: darken(#3498db, 10%);
  }
  
  &:active {
    transform: scale(0.98);
  }
}

// 编译为:
.button {
  display: inline-block;
  padding: 10px 20px;
}
.button:hover {
  background-color: #2980b9;
}
.button:active {
  transform: scale(0.98);
}

2. 伪类和伪元素

css 复制代码
.input {
  border: 1px solid #ccc;
  
  &:focus {
    border-color: #3498db;
    outline: none;
  }
  
  &::placeholder {
    color: #999;
  }
}

// 编译为:
.input {
  border: 1px solid #ccc;
}
.input:focus {
  border-color: #3498db;
  outline: none;
}
.input::placeholder {
  color: #999;
}

3. BEM命名约定

  • 中划线 :仅作为连字符使用,表示某个块或者某个子元素的多单词之间的连接记号。
    __ 双下划线:双下划线用来连接块和块的子元素
    -- 双连字符:双连字符用来描述一个块或者块的子元素的一种状态
css 复制代码
.card {
  padding: 20px;
  
  &__header {
    font-size: 18px;
  }
  
  &__content {
    margin-top: 10px;
  }
  
  &--highlighted {
    border: 2px solid #3498db;
  }
}

// 编译为:
.card {
  padding: 20px;
}
.card__header {
  font-size: 18px;
}
.card__content {
  margin-top: 10px;
}
.card--highlighted {
  border: 2px solid #3498db;
}

4. 复杂选择器组合

css 复制代码
.container {
  .item {
    // 引用两个父选择器
    .sidebar & {
      width: 100%;
    }
    
    // 使用父选择器作为前缀
    &-title {
      font-weight: bold;
    }
  }
}

// 编译为:
.container .item {
}
.sidebar .container .item {
  width: 100%;
}
.container .item-title {
  font-weight: bold;
}

嵌套属性

Sass允许您将相关的CSS属性嵌套在一个命名空间下,特别适用于带有相同前缀的属性,如 font-margin-padding- 等。

1. 基本属性嵌套

css 复制代码
.button {
  font: {
    family: Arial, sans-serif;
    size: 16px;
    weight: bold;
  }
  
  margin: {
    top: 10px;
    right: 20px;
    bottom: 10px;
    left: 20px;
  }
}

// 编译为:
.button {
  font-family: Arial, sans-serif;
  font-size: 16px;
  font-weight: bold;
  margin-top: 10px;
  margin-right: 20px;
  margin-bottom: 10px;
  margin-left: 20px;
}

2. 带有值的命名空间

css 复制代码
.text {
  border: {
    color: #333;
    style: solid;
    width: 1px;
  }
}

// 编译为:
.text {
  border-color: #333;
  border-style: solid;
  border-width: 1px;
}

3. 混合使用

css 复制代码
.box {
  padding: {
    top: 15px;
    bottom: 15px;
  }
  
  margin-left: 20px;
  margin-right: 20px;
}

// 编译为:
.box {
  padding-top: 15px;
  padding-bottom: 15px;
  margin-left: 20px;
  margin-right: 20px;
}

嵌套的最佳实践

1. 避免过度嵌套

过度嵌套会导致CSS选择器过于复杂,难以维护:

css 复制代码
// 不推荐:嵌套过深
.container {
  .sidebar {
    .widget {
      .title {
        .icon {
          // 太深了!
        }
      }
    }
  }
}

// 推荐:限制嵌套深度(通常不超过3层)
.container {
  .sidebar {
    .widget {
      .title {
        // 最多3层
      }
    }
    
    .widget-icon {
      // 单独处理深层元素
    }
  }
}

2. 使用BEM减少嵌套

BEM(Block Element Modifier)命名约定可以帮助减少嵌套深度:

css 复制代码
// 不推荐:深度嵌套
.card {
  .header {
    .title {
      .icon {
        // 太深
      }
    }
  }
}

// 推荐:使用BEM
.card {
  // 块级样式
}

.card__header {
  // 元素样式
}

.card__title {
  // 元素样式
}

.card__title-icon {
  // 元素样式
}

3. 仅在必要时嵌套

css 复制代码
// 不推荐:不必要地嵌套
.page {
  .header {
    // 如果没有共享样式,不必嵌套
  }
  
  .content {
    // 如果没有共享样式,不必嵌套
  }
}

// 推荐:扁平化结构
.page-header {
  // 直接定义
}

.page-content {
  // 直接定义
}

实际应用示例

1. 导航菜单

css 复制代码
.nav {
  background-color: #fff;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  
  &__list {
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
  }
  
  &__item {
    position: relative;
    
    &:hover {
      .nav__dropdown {
        display: block;
      }
    }
  }
  
  &__link {
    display: block;
    padding: 1rem;
    color: #333;
    text-decoration: none;
    transition: color 0.3s;
    
    &:hover {
      color: #007bff;
    }
  }
  
  &__dropdown {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 200px;
    background-color: #fff;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    
    .nav__link {
      padding: 0.75rem 1rem;
    }
  }
}

2. 表单组件

css 复制代码
.form {
  &__group {
    margin-bottom: 1.5rem;
    
    &--error {
      .form__label {
        color: #dc3545;
      }
      
      .form__input {
        border-color: #dc3545;
        
        &:focus {
          border-color: #dc3545;
          box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
        }
      }
      
      .form__error-message {
        display: block;
        color: #dc3545;
        font-size: 0.875rem;
        margin-top: 0.25rem;
      }
    }
  }
  
  &__label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 500;
  }
  
  &__input {
    display: block;
    width: 100%;
    padding: 0.75rem 1rem;
    font-size: 1rem;
    line-height: 1.5;
    color: #495057;
    background-color: #fff;
    border: 1px solid #ced4da;
    border-radius: 0.25rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
    
    &:focus {
      outline: 0;
      border-color: #80bdff;
      box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
    }
  }
  
  &__error-message {
    display: none; // 默认隐藏错误消息
  }
}

3. 响应式设计中的嵌套

css 复制代码
.card {
  padding: 1.5rem;
  border-radius: 0.25rem;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  
  @media (min-width: 768px) {
    padding: 2rem;
  }
  
  &__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1rem;
    
    @media (min-width: 768px) {
      margin-bottom: 1.5rem;
    }
  }
  
  &__title {
    margin: 0;
    font-size: 1.25rem;
    
    @media (min-width: 768px) {
      font-size: 1.5rem;
    }
  }
  
  &__content {
    color: #6c757d;
    
    p {
      margin-bottom: 1rem;
      
      &:last-child {
        margin-bottom: 0;
      }
    }
  }
}

嵌套规则与编译性能

虽然嵌套使代码更易读,但过度嵌套可能会影响编译性能和生成的CSS大小。以下是一些性能考虑:

  1. 嵌套深度与CSS特异性:嵌套越深,生成的CSS选择器特异性越高,可能导致样式难以覆盖
  2. 文件大小:过度嵌套会增加生成的CSS文件大小
  3. 编译时间:复杂的嵌套结构可能增加Sass编译时间

总结

Sass的嵌套规则和属性功能为CSS开发带来了巨大的便利:

  1. 代码组织性:按照HTML结构组织CSS,直观易懂
  2. 可维护性:相关样式集中管理,修改更容易
  3. 减少重复:避免重复编写父选择器
  4. 属性简洁性:相关属性可以组织在一起

但是,也需要注意避免过度嵌套,保持合理的嵌套深度,并选择适当的命名约定(如BEM)来平衡代码的清晰性和性能。

Sass @import 与 Partials

Sass的 @import 功能允许您将多个Sass文件合并为一个CSS文件。这有助于将样式分割为更小、更易于管理的部分,提高代码的可维护性和重用性。

与原生CSS @import的区别

  1. 性能差异

    • CSS的 @import 会在页面加载时发送额外的HTTP请求,影响性能
    • Sass的 @import 在编译时将文件内容合并,生成单个CSS文件
  2. 文件扩展名

    • Sass可以省略文件扩展名,自动查找 .scss.sass.css 文件
    • CSS必须指定完整文件名
  3. 处理方式

    • Sass在编译时处理导入
    • CSS在浏览器运行时处理导入

Partials(部分文件)

什么是Partials

Partials是以下划线( _ )开头的Sass文件,它们不会被直接编译为CSS文件,而是专门用于被其他文件导入。

css 复制代码
// _variables.scss
$primary-color: #3498db;
$secondary-color: #2ecc71;
$font-size: 16px;

Partials的优势

  1. 防止编译冲突:不会生成单独的CSS文件
  2. 明确用途:下划线表示这是一个部分文件
  3. 文件组织:便于将相关样式分组
css 复制代码
// main.scss
@import 'variables'; // 导入_variables.scss,但不必写下划线或扩展名

body {
  color: $primary-color;
  font-size: $font-size;
}

@import 语法与用法

基本语法

css 复制代码
@import "filename"; // 使用引号
@import 'filename'; // 使用单引号也可以
@import filename;    // 不使用引号也可以(不推荐)

文件搜索顺序

当您导入一个文件时,Sass会按以下顺序查找:

  1. _filename.scss
  2. _filename.sass
  3. filename.scss
  4. filename.sass
  5. filename.css

导入多个文件

css 复制代码
@import "variables", "mixins", "reset";
// 等同于:
@import "variables";
@import "mixins";
@import "reset";

文件组织结构

典型的Sass项目结构

css 复制代码
scss/
├── main.scss              // 主文件,导入所有部分文件
├── _variables.scss        // 变量定义
├── _mixins.scss           // 混合宏
├── _functions.scss        // 自定义函数
├── _placeholders.scss     // 占位符选择器
├── base/
│   ├── _reset.scss        // 重置样式
│   ├── _typography.scss   // 排版样式
│   └── _base.scss         // 基础样式
├── components/
│   ├── _buttons.scss      // 按钮样式
│   ├── _cards.scss        // 卡片样式
│   └── _forms.scss        // 表单样式
├── layout/
│   ├── _header.scss       // 头部布局
│   ├── _footer.scss       // 底部布局
│   └── _grid.scss         // 网格系统
└── pages/
    ├── _home.scss         // 首页样式
    └── _about.scss        // 关于页面样式

main.scss 示例

css 复制代码
// 清理CSS
@charset "utf-8";

// 导入变量和函数
@import "variables";
@import "functions";
@import "mixins";
@import "placeholders";

// 导入基础样式
@import "base/reset";
@import "base/typography";
@import "base/base";

// 导入布局
@import "layout/header";
@import "layout/footer";
@import "layout/grid";

// 导入组件
@import "components/buttons";
@import "components/cards";
@import "components/forms";

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

导入不同类型的文件

1. 导入Sass/SCSS文件

css 复制代码
@import "variables"; // 导入_variables.scss
@import "mixins/utils"; // 导入_mixins/utils.scss

2. 导入CSS文件

css 复制代码
// 直接导入CSS文件,内容会被原样包含在输出中
@import "normalize.css";

3. 导入URL

css 复制代码
// 导入外部CSS文件
@import url("https://fonts.googleapis.com/css?family=Open+Sans");

导入的作用域

变量作用域

css 复制代码
// _variables.scss
$primary-color: #3498db;

// _buttons.scss
@import "variables"; // 导入变量

.button {
  background-color: $primary-color; // 可以使用导入的变量
}

// main.scss
@import "variables";
@import "buttons";

.header {
  background-color: $primary-color; // 也可以使用
}

变量覆盖

导入顺序很重要,后导入的文件会覆盖先导入文件中的变量

css 复制代码
// _variables.scss
$primary-color: #3498db;

// _theme.scss
$primary-color: #e74c3c; // 覆盖前面的变量

// main.scss
@import "variables";
@import "theme";

.button {
  background-color: $primary-color; // 使用#e74c3c
}

使用!default防止覆盖

使用 !default 标志可以防止变量被意外覆盖:

css 复制代码
// _variables.scss
$primary-color: #3498db !default;
$font-size: 16px !default;

// main.scss
$primary-color: #e74c3c; // 这会覆盖默认值

@import "variables"; // 由于$primary-color已定义,不会使用默认值

// 但如果没有在前面定义,$font-size会使用默认值16px

条件导入

使用@if控制导入

css 复制代码
// 根据变量决定导入哪个主题文件
@if $theme == "light" {
  @import "themes/light";
} @else if $theme == "dark" {
  @import "themes/dark";
} @else {
  @import "themes/default";
}

使用@import的optional标志

css 复制代码
// 尝试导入文件,如果不存在不会报错
@import "optional-file" as *;

高级用法

1. 嵌套导入

css 复制代码
// _theme.scss
.theme-light {
  background-color: #fff;
  color: #333;
}

// main.scss
.container {
  @import "theme"; // 在.container内导入
  
  .content {
    padding: 20px;
  }
}

// 编译为:
.container .theme-light {
  background-color: #fff;
  color: #333;
}

.container .content {
  padding: 20px;
}

2. 添加命名空间

css 复制代码
// 导入文件并添加命名空间
@import "variables" as vars;

.button {
  background-color: vars.$primary-color; // 使用命名空间访问变量
}

最佳实践

1. 文件命名

  • 使用下划线前缀( _ )表示部分文件
  • 使用小写字母和连字符命名文件
  • 保持文件名描述性强
css 复制代码
_variables.scss      // 好的命名
_buttons.scss        // 好的命名
form-styles.scss    // 好的命名
VARs.scss           // 不好的命名(大写字母)
btn.scss            // 不好的命名(缩写不清晰)

2. 导入顺序

  1. 首先导入变量、函数和混合宏
  2. 然后导入基础样式和重置
  3. 接着导入布局和组件
  4. 最后导入页面特定样式和覆盖
css 复制代码
// 标准导入顺序
@import "variables";
@import "functions";
@import "mixins";

@import "base/reset";
@import "base/typography";

@import "layout/grid";
@import "layout/header";

@import "components/buttons";
@import "components/cards";

@import "pages/home";

3. 避免循环依赖

确保文件之间没有循环导入,这会导致编译错误:

css 复制代码
// _file1.scss
@import "file2"; // 导入file2

// _file2.scss
@import "file1"; // 错误:循环导入

4. 使用索引文件简化导入

创建 _index.scss 文件来简化目录导入:

css 复制代码
components/
├── _index.scss    // 索引文件,导入目录下所有部分文件
├── _buttons.scss
├── _cards.scss
└── _forms.scss
css 复制代码
// components/_index.scss
@import "buttons";
@import "cards";
@import "forms";

// main.scss
@import "components"; // 导入整个components目录

实际应用示例

1. 创建一个变量库

css 复制代码
// _variables.scss
// 颜色
$primary-color: #3498db !default;
$secondary-color: #2ecc71 !default;
$success-color: #27ae60 !default;
$warning-color: #f39c12 !default;
$error-color: #e74c3c !default;

// 字体
$font-family-base: "Helvetica Neue", Arial, sans-serif !default;
$font-size-base: 16px !default;
$line-height-base: 1.5 !default;

// 间距
$spacer: 1rem !default;
$spacer-1: $spacer !default;    // 1rem
$spacer-2: ($spacer * 1.5) !default;  // 1.5rem
$spacer-3: ($spacer * 2) !default;    // 2rem

// 断点
$breakpoint-sm: 576px !default;
$breakpoint-md: 768px !default;
$breakpoint-lg: 992px !default;
$breakpoint-xl: 1200px !default;

2. 创建一个混合宏库

css 复制代码
// _mixins.scss
// 导入变量
@import "variables";

// 响应式断点混合宏
@mixin respond-to($breakpoint) {
  @if $breakpoint == sm {
    @media (min-width: $breakpoint-sm) {
      @content;
    }
  } @else if $breakpoint == md {
    @media (min-width: $breakpoint-md) {
      @content;
    }
  } @else if $breakpoint == lg {
    @media (min-width: $breakpoint-lg) {
      @content;
    }
  } @else if $breakpoint == xl {
    @media (min-width: $breakpoint-xl) {
      @content;
    }
  }
}

// 按钮样式混合宏
@mixin button-variant($bg-color, $color: white) {
  background-color: $bg-color;
  color: $color;
  border: 1px solid $bg-color;
  
  &:hover,
  &:focus {
    background-color: darken($bg-color, 10%);
    border-color: darken($bg-color, 10%);
  }
  
  &:active {
    background-color: darken($bg-color, 15%);
    border-color: darken($bg-color, 15%);
  }
}

3. 使用模块化组织样式

css 复制代码
// main.scss
@import "variables";
@import "mixins";

@import "base/reset";
@import "base/typography";

@import "layout/grid";
@import "layout/header";
@import "layout/footer";

@import "components/buttons";
@import "components/cards";
@import "components/navigation";

// 自定义样式
.hero {
  background-color: $primary-color;
  padding: $spacer-3 0;
  
  .title {
    color: white;
    font-size: ($font-size-base * 2);
    
    @include respond-to(md) {
      font-size: ($font-size-base * 3);
    }
  }
  
  .button {
    @include button-variant($secondary-color);
    
    &-alt {
      @include button-variant(white, $primary-color);
    }
  }
}

现代CSS @import vs Sass @import

随着CSS模块化的需求增加,现代CSS也引入了 @import 语法,但与Sass的 @import 有所不同:

特性 Sass @import CSS @import
处理时间 编译时 运行时
性能影响 无额外请求 产生额外HTTP请求
文件合并
文件扩展名 可省略 必须指定
支持功能 变量、混合等 仅CSS

总结

Sass的 @import 与Partials功能提供了一种强大的方式来组织和模块化CSS代码:

  1. 代码组织:将大型样式表分解为更小、更易于管理的部分
  2. 重用性:创建可重用的变量、混合和函数库
  3. 维护性:相关样式分组,便于查找和修改
  4. 团队协作:团队成员可以并行开发不同的部分文件

通过合理使用 @import 和Partials,可以构建出结构清晰、易于维护的Sass项目,提高开发效率和代码质量。

Sass @mixin 与 @include

Sass Mixin(混合)概述

Mixin是Sass中用于定义可重用CSS规则块的功能,类似于编程语言中的函数。它允许您创建包含CSS声明的模板,然后在需要的地方将其包含进来。

Mixin与普通CSS类的区别

  1. Mixin

    • 在编译时展开,生成具体的CSS规则
    • 可以接受参数
    • 不会直接出现在最终的CSS中
  2. CSS类

    • 直接存在于CSS中
    • 通过HTML的class属性应用
    • 不能接受参数

定义和使用Mixin

1. 基本Mixin定义与使用

css 复制代码
// 定义一个简单的Mixin
@mixin center-block {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

// 使用Mixin
.container {
  @include center-block;
}

// 编译为:
.container {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

2. 带参数的Mixin

css 复制代码
// 定义带参数的Mixin
@mixin border-radius($radius) {
  border-radius: $radius;
  -webkit-border-radius: $radius;
  -moz-border-radius: $radius;
}

// 使用带参数的Mixin
.button {
  @include border-radius(5px);
}

.box {
  @include border-radius(10px);
}

// 编译为:
.button {
  border-radius: 5px;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}
.box {
  border-radius: 10px;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
}

3. 带默认参数的Mixin

css 复制代码
// 定义带默认参数的Mixin
@mixin transition($property: all, $duration: 0.3s, $timing: ease) {
  transition: $property $duration $timing;
  -webkit-transition: $property $duration $timing;
  -moz-transition: $property $duration $timing;
}

// 使用不同参数的Mixin
.button {
  @include transition; // 使用所有默认参数
}

.card {
  @include transition(opacity, 0.5s); // 覆盖前两个参数
}

.modal {
  @include transition(transform, 0.4s, ease-out); // 覆盖所有参数
}

4. 可变参数的Mixin

css 复制代码
// 使用...定义可变参数
@mixin box-shadow($shadows...) {
  box-shadow: $shadows;
  -webkit-box-shadow: $shadows;
  -moz-box-shadow: $shadows;
}

// 使用可变参数
.card {
  @include box-shadow(0 4px 8px rgba(0, 0, 0, 0.1));
}

.button {
  @include box-shadow(0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.5));
}

Mixin高级用法

1. 条件语句

css 复制代码
// 定义带条件判断的Mixin
@mixin text-overflow($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
.title-single {
  @include text-overflow(1); // 单行省略
}

.title-multi {
  @include text-overflow(3); // 三行省略
}

2. 循环语句

css 复制代码
// 定义带循环的Mixin
@mixin generate-sizes($prefix, $sizes...) {
  @each $size in $sizes {
    .#{$prefix}-#{$size} {
      font-size: #{$size}px;
    }
  }
}

// 使用循环Mixin
@include generate-sizes(text, 12, 14, 16, 18, 20);

// 编译为:
.text-12 { font-size: 12px; }
.text-14 { font-size: 14px; }
.text-16 { font-size: 16px; }
.text-18 { font-size: 18px; }
.text-20 { font-size: 20px; }

3. Mixin内容块

css 复制代码
// 定义接受内容块的Mixin
@mixin screen($size) {
  @media (min-width: $size) {
    @content; // 插入传入的内容块
  }
}

// 使用内容块
.container {
  width: 100%;
  
  @include screen(768px) {
    width: 750px;
  }
  
  @include screen(992px) {
    width: 970px;
  }
}

// 编译为:
.container {
  width: 100%;
}
@media (min-width: 768px) {
  .container {
    width: 750px;
  }
}
@media (min-width: 992px) {
  .container {
    width: 970px;
  }
}

4. 变量作用域

css 复制代码
// 全局变量
$default-color: #333;

@mixin theme-style($theme-color) {
  // Mixin内部变量
  $local-color: $theme-color;
  
  color: $local-color;
  
  .child {
    // 可以访问Mixin内部的变量
    background-color: lighten($local-color, 40%);
  }
}

.button {
  @include theme-style($default-color);
}

实用Mixin示例

1. 响应式设计Mixin

css 复制代码
// 定义断点变量
$breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

// 响应式Mixin
@mixin respond-to($breakpoint) {
  @if map-has-key($breakpoints, $breakpoint) {
    @media (min-width: map-get($breakpoints, $breakpoint)) {
      @content;
    }
  } @else {
    @warn "未知的断点: #{$breakpoint}. 可用断点: #{map-keys($breakpoints)}";
  }
}

// 使用响应式Mixin
.container {
  padding: 15px;
  
  @include respond-to(sm) {
    padding: 20px;
  }
  
  @include respond-to(md) {
    padding: 30px;
  }
  
  @include respond-to(lg) {
    padding: 40px;
  }
}

2. 按钮样式Mixin

css 复制代码
// 按钮基础样式Mixin
@mixin button-base {
  display: inline-block;
  padding: 10px 20px;
  font-size: 16px;
  font-weight: 500;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 4px;
  transition: all 0.2s ease-in-out;
  
  &:focus {
    outline: 0;
    box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
  }
  
  &:disabled {
    opacity: 0.65;
    cursor: not-allowed;
  }
}

// 按钮变体Mixin
@mixin button-variant($bg-color, $color: white) {
  background-color: $bg-color;
  color: $color;
  border-color: $bg-color;
  
  &:hover:not(:disabled) {
    background-color: darken($bg-color, 7.5%);
    border-color: darken($bg-color, 10%);
  }
  
  &:active:not(:disabled) {
    background-color: darken($bg-color, 10%);
    border-color: darken($bg-color, 12.5%);
  }
}

// 使用按钮Mixin
.btn {
  @include button-base;
}

.btn-primary {
  @include button-variant(#007bff);
}

.btn-success {
  @include button-variant(#28a745);
}

.btn-outline {
  background-color: transparent;
  color: #007bff;
  border-color: #007bff;
  
  @include button-variant(transparent, #007bff);
}

3. 网格系统Mixin

css 复制代码
// 网格容器Mixin
@mixin make-container {
  width: 100%;
  padding-right: 15px;
  padding-left: 15px;
  margin-right: auto;
  margin-left: auto;
}

// 网格行Mixin
@mixin make-row {
  display: flex;
  flex-wrap: wrap;
  margin-right: -15px;
  margin-left: -15px;
}

// 网格列Mixin
@mixin make-col($size, $columns: 12) {
  flex: 0 0 percentage($size / $columns);
  max-width: percentage($size / $columns);
  padding-right: 15px;
  padding-left: 15px;
}

// 响应式网格列Mixin
@mixin make-col-responsive($size, $breakpoint: null) {
  @if $breakpoint {
    @include respond-to($breakpoint) {
      @include make-col($size);
    }
  } @else {
    @include make-col($size);
  }
}

// 使用网格Mixin
.container {
  @include make-container;
}

.row {
  @include make-row;
}

.col {
  @include make-col(12);
  
  @include respond-to(sm) {
    @include make-col(6);
  }
  
  @include respond-to(md) {
    @include make-col(4);
  }
}

.col-8 {
  @include make-col(8);
}

.col-md-6 {
  @include make-col-responsive(6, md);
}

Mixin的最佳实践

1. 合理命名

css 复制代码
// 好的命名:描述性强,清晰易懂
@mixin button-variant($bg-color, $color: white) { ... }
@mixin respond-to($breakpoint) { ... }
@mixin flex-center { ... }

// 不好的命名:缩写不清晰,用途不明
@mixin btn-var($c, $t: w) { ... }
@mixin resp($bp) { ... }

2. 参数验证

css 复制代码
// 带参数验证的Mixin
@mixin font-size($size) {
  @if type-of($size) != number {
    @error "字体大小必须是数字,但传递的是 #{type-of($size)}";
  }
  
  @if not unitless($size) and unit($size) != px and unit($size) != rem {
    @error "字体大小单位必须是px或rem";
  }
  
  font-size: $size;
}

3. 避免过度复杂

css 复制代码
// 不推荐:过于复杂的Mixin
@mixin complex-button($size, $color, $hover-color, $text-color, $border-radius, $padding, $shadow) {
  // 大量复杂的代码...
}

// 推荐:分解为多个简单的Mixin
@mixin button-base { ... }
@mixin button-size($size, $padding) { ... }
@mixin button-variant($color, $hover-color, $text-color) { ... }
@mixin button-shape($border-radius) { ... }
@mixin button-shadow($shadow) { ... }

// 组合使用简单的Mixin
.btn {
  @include button-base;
  @include button-size(medium, 10px 20px);
  @include button-variant(blue, darkblue, white);
  @include button-shape(4px);
  @include button-shadow(0 2px 5px rgba(0,0,0,0.1));
}

4. 与变量结合使用

css 复制代码
// 定义变量
$button-sizes: (
  small: 8px 16px,
  medium: 10px 20px,
  large: 12px 24px
);

// 使用变量
@mixin button-size($size) {
  @if map-has-key($button-sizes, $size) {
    padding: map-get($button-sizes, $size);
  } @else {
    @warn "未知的按钮大小: #{$size}";
  }
}

Mixin与@extend的比较

特性 Mixin @extend
代码重复 每次使用都会复制代码 合并选择器,减少重复
参数支持 支持参数传递 不支持参数
内容块 支持@content 不支持
选择器关系 独立规则 继承关系
使用场景 动态样式、需要参数 静态样式、共享属性

Mixin的局限性

  1. 代码重复:每次使用Mixin都会复制完整的代码,可能导致CSS变大
  2. 特异性问题:可能导致选择器特异性增加
  3. 调试困难:生成的CSS可能不直观,增加调试难度

总结

Sass的 @mixin@include 为CSS开发提供了强大的代码重用和封装能力:

  1. 代码复用:避免重复编写相似的CSS代码
  2. 参数化:通过参数实现样式定制
  3. 功能封装:将复杂逻辑封装在Mixin中
  4. 条件逻辑:支持条件判断和循环
  5. 响应式设计:简化媒体查询编写

合理使用Mixin可以大大提高CSS的可维护性和开发效率,但也要注意避免过度复杂化和代码膨胀。

Sass @extend 与 继承

Sass @extend 概述

@extend 是Sass中用于继承已有选择器样式的功能。它允许一个选择器继承另一个选择器的所有样式,从而创建样式的继承关系,减少代码重复。

@extend 与 Mixin 的区别

特性 @extend Mixin
代码生成 合并选择器 复制代码
参数支持 不支持 支持
样式关系 继承关系 独立规则
生成的CSS 优化选择器 可能产生重复

基本用法

1. 简单继承

css 复制代码
// 基础样式
.message {
  padding: 10px 15px;
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 10px;
}

// 继承基础样式
.success {
  @extend .message;
  background-color: #dff0d8;
  border-color: #d6e9c6;
  color: #3c763d;
}

.error {
  @extend .message;
  background-color: #f2dede;
  border-color: #ebccd1;
  color: #a94442;
}

// 编译为:
.message, .success, .error {
  padding: 10px 15px;
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 10px;
}

.success {
  background-color: #dff0d8;
  border-color: #d6e9c6;
  color: #3c763d;
}

.error {
  background-color: #f2dede;
  border-color: #ebccd1;
  color: #a94442;
}

2. 多重继承

css 复制代码
// 基础样式
.btn-base {
  display: inline-block;
  padding: 6px 12px;
  font-size: 14px;
  font-weight: normal;
  line-height: 1.42857143;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  user-select: none;
  border: 1px solid transparent;
  border-radius: 4px;
}

.btn-large {
  padding: 10px 16px;
  font-size: 18px;
  line-height: 1.3333333;
  border-radius: 6px;
}

// 继承多个选择器
.btn-primary {
  @extend .btn-base;
  @extend .btn-large;
  color: #fff;
  background-color: #337ab7;
  border-color: #2e6da4;
}

// 编译为:
.btn-base, .btn-primary {
  display: inline-block;
  padding: 6px 12px;
  font-size: 14px;
  font-weight: normal;
  line-height: 1.42857143;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  user-select: none;
  border: 1px solid transparent;
  border-radius: 4px;
}

.btn-large, .btn-primary {
  padding: 10px 16px;
  font-size: 18px;
  line-height: 1.3333333;
  border-radius: 6px;
}

.btn-primary {
  color: #fff;
  background-color: #337ab7;
  border-color: #2e6da4;
}

占位符选择器

占位符选择器是 @extend 的最佳实践,它定义不会被编译到CSS中的样式模板,只用于被其他选择器继承。

占位符语法

占位符选择器以 % 开头,类似于ID选择器使用 # 和类选择器使用 .

css 复制代码
// 定义占位符
%btn-base {
  display: inline-block;
  padding: 6px 12px;
  font-size: 14px;
  text-align: center;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 4px;
}

// 继承占位符
.btn-primary {
  @extend %btn-base;
  background-color: #337ab7;
  color: white;
}

.btn-secondary {
  @extend %btn-base;
  background-color: #6c757d;
  color: white;
}

// 编译为(注意:没有生成.btn-base选择器):
.btn-primary, .btn-secondary {
  display: inline-block;
  padding: 6px 12px;
  font-size: 14px;
  text-align: center;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 4px;
}

.btn-primary {
  background-color: #337ab7;
  color: white;
}

.btn-secondary {
  background-color: #6c757d;
  color: white;
}

占位符 vs 普通选择器

css 复制代码
// 不推荐:使用普通选择器作为基础
.panel {
  padding: 15px;
  margin-bottom: 20px;
  background-color: #fff;
  border: 1px solid transparent;
  border-radius: 4px;
}

.panel-default {
  @extend .panel;
  border-color: #ddd;
}

// 问题:.panel选择器出现在最终CSS中,即使可能不需要它

// 推荐:使用占位符选择器
%panel {
  padding: 15px;
  margin-bottom: 20px;
  background-color: #fff;
  border: 1px solid transparent;
  border-radius: 4px;
}

.panel-default {
  @extend %panel;
  border-color: #ddd;
}

// 优点:%panel不会出现在最终CSS中,减少不必要的样式

高级用法

1. 链式继承

css 复制代码
// 基础样式
%base-button {
  display: inline-block;
  padding: 10px 15px;
  font-size: 14px;
  text-align: center;
  cursor: pointer;
  border-radius: 4px;
  border: 1px solid transparent;
}

// 中间样式
%primary-button {
  @extend %base-button;
  background-color: #007bff;
  color: white;
}

// 具体样式
.button {
  @extend %primary-button;
  
  &:hover {
    background-color: #0069d9;
  }
}

.button-large {
  @extend %button;
  padding: 12px 20px;
  font-size: 16px;
}

// 编译为:
%base-button, .button, .button-large {
  display: inline-block;
  padding: 10px 15px;
  font-size: 14px;
  text-align: center;
  cursor: pointer;
  border-radius: 4px;
  border: 1px solid transparent;
}

%primary-button, .button, .button-large {
  background-color: #007bff;
  color: white;
}

.button, .button-large {
  // 注意:这部分实际上是合并了所有继承链
}

.button {
  // button特有的样式
}

.button-large {
  padding: 12px 20px;
  font-size: 16px;
}

2. 继承与嵌套

css 复制代码
// 基础样式
%block {
  margin-bottom: 20px;
  
  %title {
    font-size: 18px;
    font-weight: bold;
    margin-bottom: 10px;
  }
  
  %content {
    font-size: 14px;
    line-height: 1.5;
  }
}

// 继承嵌套样式
.card {
  @extend %block;
  
  .title {
    @extend %title;
  }
  
  .content {
    @extend %content;
  }
}

.alert {
  @extend %block;
  padding: 15px;
  border: 1px solid transparent;
  border-radius: 4px;
  
  .message {
    @extend %content;
  }
}

// 编译为(注意生成的选择器组合):
%block, .card, .alert {
  margin-bottom: 20px;
}

%block %title, .card .title {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 10px;
}

%block %content, .card .content, .alert .message {
  font-size: 14px;
  line-height: 1.5;
}

.alert {
  padding: 15px;
  border: 1px solid transparent;
  border-radius: 4px;
}

3. 可选继承

css 复制代码
// 基础样式
%button-base {
  display: inline-block;
  padding: 6px 12px;
  font-size: 14px;
  text-align: center;
  cursor: pointer;
}

// 使用可选继承(当选择器可能不存在时)
.custom-button {
  @extend %button-base !optional;
  background-color: #f8f9fa;
}

4. 占位符与混合结合

css 复制代码
// 定义占位符和混合
%flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

@mixin button-style($bg-color, $text-color: white) {
  background-color: $bg-color;
  color: $text-color;
  
  &:hover {
    background-color: darken($bg-color, 10%);
  }
}

// 组合使用
.button {
  @extend %flex-center;
  @include button-style(#007bff);
}

实际应用示例

1. 表单控件样式

css 复制代码
// 基础表单控件样式
%form-control {
  display: block;
  width: 100%;
  padding: 8px 12px;
  font-size: 14px;
  line-height: 1.42857143;
  color: #555;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 4px;
  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
  
  &:focus {
    border-color: #66afe9;
    outline: 0;
    box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6);
  }
}

// 具体表单控件
.input {
  @extend %form-control;
}

.textarea {
  @extend %form-control;
  height: auto;
  resize: vertical;
}

.select {
  @extend %form-control;
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23333' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .5rem center;
  background-size: 8px 10px;
  padding-right: 25px;
}

2. 布局组件

css 复制代码
// 基础布局样式
%flex-container {
  display: flex;
}

%flex-column {
  flex-direction: column;
}

%flex-wrap {
  flex-wrap: wrap;
}

%flex-center {
  align-items: center;
  justify-content: center;
}

%flex-space-between {
  justify-content: space-between;
}

// 具体布局组件
.flex-container {
  @extend %flex-container;
}

.flex-center {
  @extend %flex-container;
  @extend %flex-center;
}

.flex-column {
  @extend %flex-container;
  @extend %flex-column;
}

.flex-wrap {
  @extend %flex-container;
  @extend %flex-wrap;
}

.nav {
  @extend %flex-container;
  @extend %flex-space-between;
  
  &.vertical {
    @extend %flex-column;
  }
}

3. 状态样式

css 复制代码
// 基础状态样式
%disabled {
  opacity: .65;
  cursor: not-allowed;
  pointer-events: none;
}

%active {
  background-image: none;
  outline: 0;
  box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}

// 应用于不同组件
.button {
  // 正常状态样式...
  
  &:disabled {
    @extend %disabled;
  }
  
  &:active {
    @extend %active;
  }
}

.input {
  // 正常状态样式...
  
  &:disabled {
    @extend %disabled;
    background-color: #eee;
  }
}

@extend的最佳实践

1. 使用占位符选择器

css 复制代码
// 不推荐:使用普通选择器作为基础
.base-button {
  // 样式...
}

// 推荐:使用占位符选择器
%base-button {
  // 样式...
}

2. 避免复杂的选择器链

css 复制代码
// 不推荐:复杂的选择器链
.nav .item .link {
  // 样式...
}

.special-nav .item .link {
  @extend .nav .item .link; // 复杂的继承链
}

// 推荐:使用占位符和简单的结构
%nav-link {
  // 样式...
}

.nav .item .link {
  @extend %nav-link;
}

.special-nav .item .link {
  @extend %nav-link;
}

3. 避免跨文件继承

css 复制代码
// file1.scss
%base-style {
  // 样式...
}

// file2.scss
.component {
  @extend %base-style; // 不推荐:跨文件继承
}

// 推荐:确保导入顺序正确
// main.scss
@import "base-style";
@import "components";

4. 结合BEM命名约定

css 复制代码
// 使用占位符定义基础样式
%block {
  box-sizing: border-box;
}

%element {
  // 元素基础样式...
}

%modifier {
  // 修饰符基础样式...
}

// 应用BEM
.card {
  @extend %block;
  padding: 20px;
  border-radius: 4px;
}

.card__header {
  @extend %element;
  margin-bottom: 15px;
}

.card__title {
  @extend %element;
  font-size: 18px;
  font-weight: bold;
}

.card--highlighted {
  @extend %modifier;
  border: 1px solid #007bff;
}

@extend的局限性

  1. 选择器特异性:继承可能导致选择器特异性增加,难以覆盖
  2. 复杂选择器:复杂的选择器继承可能导致意外的CSS规则
  3. 文件顺序依赖:跨文件继承需要正确的导入顺序
  4. 调试困难:生成的CSS选择器可能不直观,增加调试难度

总结

Sass的 @extend 与继承功能提供了一种优雅的方式来重用样式和维护选择器关系:

  1. 代码复用:通过继承减少重复代码
  2. 维护性:修改基础样式,所有继承的选择器都会更新
  3. 语义关系:保持选择器之间的语义关系
  4. CSS优化:生成更紧凑的CSS,合并相似选择器

合理使用 @extend (特别是占位符选择器)可以创建出结构清晰、易于维护的样式表。但也要注意避免过度复杂化和潜在的特异性问题。结合 @mixin 、变量和函数,Sass提供了强大的工具集来构建高效、可维护的CSS代码。

Sass 函数

Sass 函数概述

Sass函数类似于编程语言中的函数,它接受参数,执行操作,并返回一个值。Sass提供了丰富的内置函数,同时也允许您创建自定义函数。

函数与Mixins的区别

特性 函数 Mixin
返回值 返回单个值 生成CSS代码块
使用方式 表达式/属性值 @include指令
主要用途 计算、转换、操作 样式复用、代码生成

内置函数

Sass提供了丰富的内置函数,分为多个类别:

1. 字符串函数

css 复制代码
// 字符串长度
$str-length: str-length("Hello, Sass!"); // 12

// 字符串插入
$insert: str-insert("Hello World", "Beautiful ", 6); // "Hello Beautiful World"

// 字符串索引
$index: str-index("Hello", "e"); // 2

// 字符串切片
$slice: str-slice("Hello, World!", 1, 5); // "Hello"

// 转换大小写
$upper: to-upper-case("hello"); // "HELLO"
$lower: to_lower_case("WORLD"); // "world"

// 引用与去引
$quote: quote("Hello"); // "Hello"
$unquote: unquote("Hello"); // Hello

// 使用示例
.element {
  content: quote("这是一段引用文本");
  &::before {
    content: str-insert("开始", "和结束", 2);
  }
}

2. 数字函数

css 复制代码
// 绝对值
$abs: abs(-15px); // 15px

// 四舍五入
$round: round(15.7px); // 16px
$ceil: ceil(15.2px); // 16px
$floor: floor(15.9px); // 15px

// 百分比转换
$percentage: percentage(0.5); // 50%

// 最小值和最大值
$min: min(10px, 20px, 5px); // 5px
$max: max(10px, 20px, 5px); // 20px

// 单位处理
$unit: unit(15px); // "px"
$unitless: unitless(15); // true
$compatible: is-compatible(10px, 1em); // true

// 使用示例
.container {
  width: max(300px, 50%);
  margin: round(15.7px);
}

3. 列表函数

css 复制代码
// 创建列表
$list: 10px 20px 30px;
$comma-list: ("Arial", "Helvetica", sans-serif);

// 列表长度
$length: length(10px 20px 30px); // 3

// 获取列表元素
$nth: nth(10px 20px 30px, 2); // 20px

// 设置列表元素
$set-nth: set-nth(10px 20px 30px, 2, 25px); // 10px 25px 30px

// 追加元素
$append: append(10px 20px, 30px); // 10px 20px 30px

// 连接列表
$join: join(10px 20px, 30px 40px); // 10px 20px 30px 40px

// 列表索引
$index: index(a b c d, b); // 2

// 使用示例
$margin-values: 10px 15px 20px 25px;
.box {
  margin-top: nth($margin-values, 1); // 10px
  margin-right: nth($margin-values, 2); // 15px
  margin-bottom: nth($margin-values, 3); // 20px
  margin-left: nth($margin-values, 4); // 25px
}

4. Map函数

css 复制代码
// 创建Map
$colors: (
  "primary": #3498db,
  "secondary": #2ecc71,
  "success": #27ae60
);

// 获取Map值
$primary: map-get($colors, "primary"); // #3498db

// 检查键是否存在
$has-key: map-has-key($colors, "primary"); // true
$no-key: map-has-key($colors, "warning"); // false

// 获取所有键
$keys: map-keys($colors); // "primary", "secondary", "success"

// 获取所有值
$values: map-values($colors); // #3498db, #2ecc71, #27ae60

// 合并Map
$merged: map-merge($colors, ("warning": #f39c12)); // 合并后的Map

// 移除键值对
$removed: map-remove($colors, "secondary"); // 移除secondary后的Map

// 使用示例
.button {
  background-color: map-get($colors, "primary");
  color: white;
}

.alert-success {
  background-color: map-get($colors, "success");
  color: white;
}

// 使用循环遍历Map
@each $name, $color in $colors {
  .text-#{$name} {
    color: $color;
  }
}

5. 颜色函数

css 复制代码
// 获取颜色通道
$red: red(#ff6347); // 255
$green: green(#ff6347); // 99
$blue: blue(#ff6347); // 71
$hue: hue(#ff6347); // 9deg
$saturation: saturation(#ff6347); // 100%
$lightness: lightness(#ff6347); // 63%

// 调整颜色
$lighten: lighten(#ff6347, 20%); // 变亮
$darken: darken(#ff6347, 20%); // 变暗
$saturate: saturate(#ff6347, 20%); // 增加饱和度
$desaturate: desaturate(#ff6347, 20%); // 降低饱和度
$grayscale: grayscale(#ff6347); // 转为灰度
$invert: invert(#ff6347); // 反转颜色

// 调整色相
$adjust-hue: adjust-hue(#ff6347, 60deg); // 调整色相

// 混合颜色
$mix: mix(#ff6347, #0000ff, 50%); // 混合两种颜色

// 透明度处理
$rgba: rgba(#ff6347, 0.5); // 添加透明度
$alpha: alpha(rgba(255, 99, 71, 0.5)); // 0.5
$opacify: opacify(rgba(255, 99, 71, 0.5), 0.3); // 增加不透明度
$transparentize: transparentize(rgba(255, 99, 71, 0.5), 0.3); // 增加透明度

// 使用示例
$primary-color: #3498db;

.button {
  background-color: $primary-color;
  
  &:hover {
    background-color: darken($primary-color, 10%);
  }
  
  &:active {
    background-color: darken($primary-color, 15%);
  }
  
  &.outline {
    background-color: transparent;
    border: 1px solid $primary-color;
    color: $primary-color;
    
    &:hover {
      background-color: $primary-color;
      color: white;
    }
  }
}

自定义函数

1. 基本自定义函数

css 复制代码
// 定义自定义函数
@function px-to-em($px, $base-font-size: 16px) {
  @return ($px / $base-font-size) * 1em;
}

// 使用自定义函数
.heading {
  font-size: px-to-em(24px); // 1.5em
  margin-bottom: px-to-em(16px, 24px); // 0.6666666667em
}

// 带计算的函数
@function calculate-spacing($multiplier) {
  @return $multiplier * 8px;
}

.element {
  padding: calculate-spacing(2); // 16px
  margin: calculate-spacing(3); // 24px
}

2. 带条件判断的函数

css 复制代码
// 带条件判断的函数
@function text-color($bg-color) {
  @if lightness($bg-color) > 50% {
    @return #333; // 浅色背景用深色文字
  } @else {
    @return #fff; // 深色背景用浅色文字
  }
}

.button {
  background-color: #3498db;
  color: text-color(#3498db); // #fff
}

.button-light {
  background-color: #ecf0f1;
  color: text-color(#ecf0f1); // #333
}

3. 处理Map的函数

css 复制代码
// 定义主题Map
$themes: (
  "light": (
    "bg": #ffffff,
    "text": #333333,
    "primary": #3498db
  ),
  "dark": (
    "bg": #222222,
    "text": #ffffff,
    "primary": #3498db
  )
);

// 获取主题属性的函数
@function theme($theme-name, $property) {
  @if map-has-key($themes, $theme-name) {
    $theme: map-get($themes, $theme-name);
    
    @if map-has-key($theme, $property) {
      @return map-get($theme, $property);
    }
  }
  
  @return null; // 或者返回默认值
}

// 使用函数
.light-theme {
  background-color: theme("light", "bg");
  color: theme("light", "text");
}

.dark-theme {
  background-color: theme("dark", "bg");
  color: theme("dark", "text");
}

4. 生成网格系统的函数

css 复制代码
// 定义网格变量
$grid-columns: 12;
$grid-gutter: 30px;

// 计算列宽的函数
@function grid-width($columns) {
  @return percentage($columns / $grid-columns);
}

// 计算列偏移的函数
@function grid-offset($columns) {
  @return grid-width($columns);
}

// 使用函数生成网格
.col-1 { width: grid-width(1); }
.col-2 { width: grid-width(2); }
.col-3 { width: grid-width(3); }
// ... 一直到 col-12

.offset-1 { margin-left: grid-offset(1); }
.offset-2 { margin-left: grid-offset(2); }
.offset-3 { margin-left: grid-offset(3); }
// ... 一直到 offset-11

函数与Mixin的结合使用

css 复制代码
// 定义计算函数
@function strip-unit($num) {
  @if type-of($num) == "number" and not unitless($num) {
    @return $num / ($num * 0 + 1);
  }
  @return $num;
}

// 使用函数的Mixin
@mixin fluid-type($min-vw, $max-vw, $min-font-size, $max-font-size) {
  $u1: unit($min-vw);
  $u2: unit($max-vw);
  $u3: unit($min-font-size);
  $u4: unit($max-font-size);
  
  @if $u1 == $u2 and $u3 == $u4 and $u1 == $u3 {
    & {
      font-size: $min-font-size;
      
      @media screen and (min-width: $min-vw) {
        font-size: calc(
          #{$min-font-size} + #{strip-unit($max-font-size - $min-font-size)} *
            ((100vw - #{$min-vw}) / #{strip-unit($max-vw - $min-vw)})
        );
      }
      
      @media screen and (min-width: $max-vw) {
        font-size: $max-font-size;
      }
    }
  }
}

// 使用Mixin和函数组合
html {
  @include fluid-type(320px, 1200px, 14px, 18px);
}

h1 {
  @include fluid-type(320px, 1200px, 24px, 36px);
}

函数的高级应用

1. 颜色调和函数

css 复制代码
// 生成单色调色板
@function color-palette($base-color, $steps: 5) {
  $palette: ();
  
  @for $i from 1 through $steps {
    $lightness: lightness($base-color);
    $new-lightness: $lightness + ($i - 3) * 15%; // 中心点周围调整
    
    @if $new-lightness > 95% {
      $new-lightness: 95%;
    } @else if $new-lightness < 5% {
      $new-lightness: 5%;
    }
    
    $palette: map-merge($palette, (
      "step-#{$i}": change-color($base-color, $lightness: $new-lightness)
    ));
  }
  
  @return $palette;
}

// 使用调色板函数
$primary-palette: color-palette(#3498db, 5);

.button {
  background-color: map-get($primary-palette, "step-3"); // 基础色
  
  &:hover {
    background-color: map-get($primary-palette, "step-2"); // 稍暗
  }
  
  &:active {
    background-color: map-get($primary-palette, "step-1"); // 更暗
  }
  
  &.light {
    background-color: map-get($primary-palette, "step-4"); // 更亮
    
    &:hover {
      background-color: map-get($primary-palette, "step-3");
    }
  }
}

2. 响应式断点函数

css 复制代码
// 断点Map
$breakpoints: (
  "xs": 0,
  "sm": 576px,
  "md": 768px,
  "lg": 992px,
  "xl": 1200px
);

// 获取断点值的函数
@function breakpoint($name) {
  @if map-has-key($breakpoints, $name) {
    @return map-get($breakpoints, $name);
  }
  
  @warn "未知的断点: #{$name}";
  @return null;
}

// 生成媒体查询的函数
@function min-width($name) {
  @if breakpoint($name) {
    @return "min-width: #{breakpoint($name)}";
  }
}

// 使用函数的Mixin
@mixin respond-above($breakpoint) {
  @if breakpoint($breakpoint) {
    @media screen and (min-width: breakpoint($breakpoint)) {
      @content;
    }
  }
}

// 使用
.container {
  width: 100%;
  padding: 0 15px;
  
  @include respond-above("sm") {
    max-width: 540px;
    margin: 0 auto;
  }
  
  @include respond-above("md") {
    max-width: 720px;
  }
  
  @include respond-above("lg") {
    max-width: 960px;
  }
}

函数的最佳实践

1. 命名规范

css 复制代码
// 好的命名:描述性强,清晰易懂
@function px-to-rem($px, $base: 16px) { ... }
@function get-theme-color($theme, $shade: "base") { ... }
@function calculate-spacing($multiplier) { ... }

// 不好的命名:缩写不清晰,用途不明
@function p2r($px, $b: 16px) { ... }
@function gtc($t, $s: "b") { ... }
@function cs($m) { ... }

2. 参数验证

css 复制代码
// 带参数验证的函数
@function safe-add($a, $b) {
  @if type-of($a) != number or type-of($b) != number {
    @error "两个参数都必须是数字";
    @return null;
  }
  
  @if not unitless($a) and not unitless($b) and unit($a) != unit($b) {
    @error "两个数字必须有相同的单位";
    @return null;
  }
  
  @return $a + $b;
}

// 使用验证函数
.element {
  width: safe-add(100px, 50px); // 150px
  // width: safe-add(100px, "50px"); // 会报错
}

3. 默认值与可选参数

css 复制代码
// 带默认值的函数
@function create-shadow($color, $blur: 5px, $spread: 0, $opacity: 0.3) {
  $rgba-color: rgba($color, $opacity);
  @return 0 0 $blur $spread $rgba-color;
}

// 使用不同参数
.box-1 {
  box-shadow: create-shadow(#000); // 使用所有默认值
}

.box-2 {
  box-shadow: create-shadow(#000, 10px); // 覆盖blur
}

.box-3 {
  box-shadow: create-shadow(#000, 10px, 2px); // 覆盖blur和spread
}

.box-4 {
  box-shadow: create-shadow(#000, 10px, 2px, 0.5); // 覆盖所有参数
}

总结

Sass函数为CSS开发提供了强大的计算和逻辑处理能力:

  1. 计算能力:执行数学运算、颜色处理、单位转换等
  2. 代码复用:创建可重用的计算逻辑
  3. 逻辑处理:通过条件判断实现复杂逻辑
  4. 数据处理:操作列表、Map等数据结构
  5. 样式生成:与Mixin结合生成动态样式

通过合理使用Sass函数,您可以创建出更加灵活、可维护的样式系统。函数特别适合处理计算、转换和条件逻辑等场景,使CSS开发更加接近编程体验。

相关推荐
天殇凉27 分钟前
AC自动机学习笔记
java·笔记·学习
AA陈超41 分钟前
从0开始学习 **Lyra Starter Game** 项目
c++·笔记·学习·游戏·ue5·lyra
WYiQIU1 小时前
面了一次字节前端岗,我才知道何为“造火箭”的极致!
前端·javascript·vue.js·react.js·面试
qq_316837751 小时前
uniapp 观察列表每个元素的曝光时间
前端·javascript·uni-app
小夏同学呀1 小时前
在 Vue 2 中实现 “点击下载条码 → 打开新窗口预览 → 自动唤起浏览器打印” 的功能
前端·javascript·vue.js
芳草萋萋鹦鹉洲哦1 小时前
【vue】导航栏变动后刷新router的几种方法
前端·javascript·vue.js
zero13_小葵司1 小时前
JavaScript性能优化系列(八)弱网环境体验优化 - 8.3 数据预加载与缓存:提前缓存关键数据
javascript·缓存·性能优化
Irene19911 小时前
FileList 对象总结(附:不支持迭代的类数组对象表)
javascript·类数组对象·filelist·不支持迭代
rchmin2 小时前
Prompt Engineering 从入门到精通的系统学习路径
人工智能·学习·prompt