漫说前端 [ The road to be a better programmer. ]

重学 Sass

噩梦:间距从 8 的倍数全部换成 5 的倍数

在进行 UI 统一的过程中,为了达到统一的视觉效果,设计师会约定元素的间距为某一个常量的倍数,例如内外边距都要求为 8 的倍数,转换为代码即为:

/* style.css */

.box {
    margin: 8px;
    padding: 8px 16px;
}
.box2 {
    margin-left: 16px;
}
.box3 {
    padding-right: 32px;
}

......

对于每一个元素声明样式间距,显得细碎繁琐,且存在“灾难隐患”,如果设计提出“8 的倍数太大(小)了, 我们把间距的基准调整为 5(10)吧 ~”,对于野蛮生长的代码来说,这一句话意味着着极大的低效重复工作。


1. 什么是 CSS 预处理器

CSS 使用大量简单语句的集合来标示元素样式。作为样式表语言,没有编程语言的变量结构、控制语句等特性,编写低效,不可避免的产生许多重复、冗余的基础代码。

为了解决上述的问题,例如 SassLessStylus 等 CSS 预处理器应运而生,可以将支持定义嵌套、变量定义、控制语句、逻辑判断等类编程语言结构的 DSL 编译成 CSS,提高开发效率和可维护性。

2. Sass 如何解决 8 变 5 的问题

$base-margin: 8; // 变量定义
@for $i from 1 through 6 { // for 循环 从 1 到 6
  $margin: $base-margin * $i; // 变量定义
  .m#{$i}x {
    margin: #{$margin}px;
  }
  .m_t#{$i}x {
    margin-top: #{$margin}px;
  }
  .m_r#{$i}x {
    margin-right: #{$margin}px;
  }
  .m_b#{$i}x {
    margin-bottom: #{$margin}px;
  }
  .m_l#{$i}x {
    margin-left: #{$margin}px;
  }
}

编译后

.m1x {
  margin: 8px;
}
.m_t1x {
  margin-top: 8px;
}
.m_r1x {
  margin-right: 8px;
}
.m_b1x {
  margin-bottom: 8px;
}
.m_l1x {
  margin-left: 8px;
}
.m2x {
  margin: 16px;
}
.m_t2x {
  margin-top: 16px;
}
.m_r2x {
  margin-right: 16px;
}
.m_b2x {
  margin-bottom: 16px;
}
.m_l2x {
  margin-left: 16px;
}
.m3x {
  margin: 24px;
}
......

这里使用了 Sass 的变量定义,来声明了基准间距 base-margin,使用 for 循环从 1 到 6,遍历生成 1~6 倍基准间距的 margin 样式。当发生基准值变化的时候,我们只需要修改这里的 base-margin 变量定义,即可完成对整体样式间距的调整。

这是对 Sass for 循环 控制语句的一个简单应用,Sass 还提供了更多统一样式结构、简化开发的用法。基础速成推荐 阮一峰老师的 SASS用法指南

下面来看一下 Sass 进阶用法。

3.Sass 进阶

3.1 变量

$baseTextColor: #666666;

.title {
    color: $baseTextColor;
}

3.1.1 Sass 的六种数据类型

  • 数字(1.2,13,10px)
  • 文本(“qjyd”,‘foo’,abc)
  • 颜色值(#fff,rgbc(0,0,0,0.3))
  • boolean(true,false)
  • null
  • 值列表,通过空格或者逗号分割(Arial,sans-serif,0 10px 5px 10px)
  • 值映射,kv(key :value)

3.1.2 变量作用域

Sass 中变量存在块级作用域(对比 Less 的变量作用域),但不存在提升,变量必须在使用前进行定义;

$color: #fff;
.container {
    color: $color;
    $color: #000;
    .box1 {
        color: $color;
    }
    .box2 {
        $color: #ccc;
        color: $color;
    }
}
.right {
    color: $color;
}

compile

.wrap .main {
  width: 20px;
}
.wrap .sidebar {
  width: 30px;
}

.content {
  width: 10px;
}

3.2 四则运算

Sass 支持 加减乘除以及取余运算

// 数值
.box {
    $width: 10px;
    $width2: 20px;
    width: $width + $width2;
}
// 文本
.box:before {
  content: "Foo " + Bar;
}

compile

.box:before {
  content: "Foo Bar";
}

复杂运算可以使用 #{} 进行包裹,#{} 可以将变量转换为字符串,然后在选择器和属性值上可以进行拼接操作

$base-margin: 8; 
@for $i from 1 through 3 {
.m#{$i}x {
        margin: #{$base-margin * $i}px;
    }
}

compile

.m1x {
  margin: 8px;
}
.m2x {
  margin: 16px;
}
.m3x {
  margin: 24px;
}

Sass 中的除法需要特别注意,/ 运算符在 css 中也是合法符号,例如:font 在略写属性时,font-size 和 line-height 会使用 / 分割,font: 18px/1.5;,对于 CSS 中属性允许包含 / 的位置,Scss 编译不进行计算;

针对这种情况,可以使用 #{} 包裹,使用 变量、函数、括号等进行强制运算

3.3 继承

3.3.1 @extend

@extend 可以解决 CSS 代码复用的问题。举个例子,我们日常习惯使用 .clearfix 类来清除浮动,需要给每一处 HTML 结构增加类,产生 CSS 和 HTML 的耦合

.clearfix {
    clear: both;
}
.contaienr {
    @extend .clearfix;
}
.slide {
    @extend .clearfix;
    border: 1px solid #ddd;
}

compile

.clearfix, .contaienr, .slide {
  clear: both;
}
.slide {
  border: 1px solid #ddd;
}

思考,如果继承的双方是复杂选择器会发生什么呢?

.table .cell {
    color: #fff;
}
.table .box .content {
    @extend .cell;
}

3.3.2 @mixin

@mixin 类似宏,定义一段片段,可以是变量,可以是属性kv,也可以是完整的选择器和内容样式

// 基础用法
@mixin error {
    color: red;
    border-color: red;
}

.text.error {
    @include error;
}

// 指定参数 && 缺省
@mixin skin($value: #ccc) {
    color: $value;
}

.default {
    @include skin();
}
.green {
    @include skin(green);
}

compile

.text.error {
  color: red;
  border-color: red;
}
.default {
  color: #ccc;
}
.green {
  color: green;
}

3.4 控制语句

3.4.1 @for 循环

只能使用数值循环,固定循环间隔

$base-margin: 8; 
@for $i from 1 through 3 {
.m#{$i}x {
        margin: #{$base-margin * $i}px;
    }
}

compile

.m1x {
  margin: 8px;
}
.m2x {
  margin: 16px;
}
.m3x {
  margin: 24px;
}

3.4.2 @while 循环

相比 @for 循环,@while 可以自定义间隔

$i: 6;
@while $i > 0 {
  .item-#{$i} { width: 2em * $i; }
  $i: $i - 2; // 自定义 flag
}

compile

.item-6 {
  width: 12em;
}
.item-4 {
  width: 8em;
}
.item-2 {
  width: 4em;
}

3.4.3 @each 循环

@while 更灵活的循环

// 单一变量遍历
@each $item in a,b,c,d {
    .#{$item} {
        background-image: url(/image/#{$item}.jpg);
    }
}
// 变量组 遍历
@each $animal, $color, $cursor in (puma, black, default),
                                  (sea-slug, blue, pointer),
                                  (egret, white, move) {
  .#{$animal}-icon {
    background-image: url('/images/#{$animal}.png');
    border: 2px solid $color;
    cursor: $cursor;
  }
}

3.4.4 自定义函数

// px 2 rem 转换
@function px2rem($px, $base-font-size: 75px) {
  @if (unitless($px)) {
    @return px2rem($px + 0px);
  } @else if (unit($px) == rem) {
    @return $px;
  }
  @return ($px / $base-font-size) * 1rem;
}
.banner-common{
  width: 100%;
  height: px2rem(420px);
 }

compile

.banner-common {
  width: 100%;
  height: 5.6rem;
}


日常我们使用 sass,最多的是嵌套结构,变量定义,其他的特性很少触及;类似的还有框架里的进阶用法、手动 render、或者是日常习惯了但存在优雅实现的语法特性,都要我们在常规开发中,不断思考、持续摸索,打破常规、寻找快乐~ ????

作者:Deguang
创建时间:2020-06-23
修改时间:1970-01-01