呆恋小喵

Sass 学习笔记

本文以 sass 语法点为主线展开,但语法细节不在此赘述,如有疑问请移驾至 http://sass-lang.com 自行查阅。我所认为的 sass 的优势:结构化(层级关系更清晰)、组件化(公用模块的提取)、继承(公用样式的提取),希望读者能在浏览完此文后有所体会。

进入正文前,先为大家推荐一枚神奇的网站:https://www.sassmeister.com/

一、变量

变量默认值

变量默认值的价值在于进行组件化开发。

应用场景

一个站点内的公用模块在不同页面的展现形式会有些许不同,例如一个列表模块中列表项的分隔方式在不同页面存在差异。

传统做法:

/* m-module-list.css */
.module-list li {
    font-size: 14px;
    line-height: 22px;
    color: #666;
    padding: 15px;
    background-color: #FFF;
    border-bottom: 1px solid #F0F0F0;
    margin-bottom: none;
}

/* style.css */
@import 'm-module-list.css';

.module-list li {
    border-bottom: none;
    margin-bottom: 10px;
}

显然,传统做法会产生 冗余 代码。对 .module-list li 的 border-bottom 及 margin-bottom 定义了两次(很好的体现了 css 层叠 的理念)。

使用变量默认值:

/* m-module-list.scss */
$borderBottom: 1px solid #F0F0F0 !default;
$marginBottom: none !default;

.module-list {
    li {
        font-size: 14px;
        line-height: 22px;
        color: #666;
        padding: 15px;
        background-color: #FFF;
        border-bottom: $borderBottom;
        margin-bottom: $marginBottom;
    }
}

/* style.scss */
$borderBottom: none;
$marginBottom: 10px;

@import 'm-module-list';

解析后的 css 代码:

.module-list li {
    font-size: 14px;
    line-height: 22px;
    color: #666;
    padding: 15px;
    background-color: #FFF;
    border-bottom: none;
    margin-bottom: 10px;
}

上述方法是存在缺陷的:定义 全局变量 很容易造成 污染。但自 sass v3.4 便不存在这样的问题了,为什么这样讲?

先看一段示例:

$color: red;
p {
    $color: yellow;
    color: $color;
}
a {
    color: $color;
}

sass v3.3 解析后的 css 代码:

p {
    color: yellow;
}
a {
    color: yellow;
}

sass v3.4 解析后的 css 代码:

p {
    color: yellow;
}
a {
    color: red;
}

自 sass v3.4 变量的解析遵循:底层作用域内声明的变量相当于局部变量,不影响全局变量。

使用 javascript 解释:

var a = 1; 
(function () { 
    var a = 5; 
})(); 
console.log(a); // -> 1

而 sass v3.3 及以前对变量的设计思路为:在底层作用域声明变量相当于修改全局变量,它处调用该变量时会使用覆盖的新值。

使用 javascript 解释:

var a = 1; 
(function () { 
    a = 5; 
})(); 
console.log(a); // -> 5

特殊变量

特殊变量的应用十分广泛,在此不做特别介绍。请自行留意文中 #{$var} 这样的书写方式,那就是所谓的特殊变量。

多值变量

应用场景

由于存在图片失效或尺寸不合规定的情况,我们通常需要为图片添加默认底色及占位。例如浮动布局中元素尺寸的变化会扰乱呈现效果:

当然,上述问题可以通过为左侧元素清除浮动或延时加载图片等方法解决。在此,我们仅讲述为图片添加占位这一方法,效果如下:

在不同的业务场景下,占位图风格各异。例如我司的常规需求、活动类需求、专题类需求所启用的占位图就在颜色及底纹上存在差异。

我们可以定义 3 种图片类型 default、activity、special 存储在 List 类型变量 $thumbType 中,这样书写语义清晰、利于拓展、精简代码。

相关代码:

$thumbType: default, activity, special;
@each $type in $thumbType {
    %#{$type}-thumb {
        background: url("../img/#{$type}-nothumb.jpg") no-repeat 0 0;
        background-size: 100% auto;
        padding-bottom: 100%;
        height: 0;
        overflow: hidden;
    }
}

.section1 .thumb {
    @extend %default-thumb;
}
.section2 .thumb {
    @extend %activity-thumb;
}
.section3 .thumb {
    @extend %special-thumb;
}

解析后的 css 代码:

.section1 .thumb {
    background: url("../img/default-nothumb.jpg") no-repeat 0 0;
    background-size: 100% auto;
    padding-bottom: 100%;
    height: 0;
    overflow: hidden;
}
.section2 .thumb {
    background: url("../img/activity-nothumb.jpg") no-repeat 0 0;
    background-size: 100% auto;
    padding-bottom: 100%;
    height: 0;
    overflow: hidden;
}
.section3 .thumb {
    background: url("../img/special-nothumb.jpg") no-repeat 0 0;
    background-size: 100% auto;
    padding-bottom: 100%;
    height: 0;
    overflow: hidden;
}
应用场景

通常,一个站点含多套配色方案,以按钮的配色为例:主色、辅助色、弱色。每套配色又由多种状态色组成:常态色、点击色、失效色。

我们可以将这些色值按照一定的规则储存在 Map 类型变量 中,这样做的优势为:

相关代码:

$btnColor: (
    main: (
        normal: (
            font: #C53336,
            border: #C53336,
            background: #FFF
        ),
        active: (
            font: #FFF,
            border: #C53336,
            background: #C53336
        ),
        disabled: (
            font: #999,
            border: #F5F5F5,
            background: #F5F5F5
        )
    ),
    weak: (
        normal: (
            font: #333,
            border: #666,
            background: #FFF
        ),
        active: (
            font: #FFF,
            border: #666,
            background: #666
        ),
        disabled: (
            font: #999,
            border: #CCC,
            background: #FFF
        )
    )
);
@mixin btnColor ($font, $border, $background) {
    color: $font;
    border-color: $border;
    background-color: $background;
}
@each $type, $status in $btnColor {
    %btn-#{$type} {
        $normal: map-get($status, normal);
        $active: map-get($status, active);
        $disabled: map-get($status, disabled);
        
        @include btnColor(map-values($normal)...);
        
        &:active {
            @include btnColor(map-values($active)...);
        }
        &.disabled {
            @include btnColor(map-values($disabled)...);
        }
    }
}

a.btn-main {
    @extend %btn-main;
}
a.btn-weak {
    @extend %btn-weak;
}

解析后的 css 代码:

a.btn-main {
    color: #C53336;
    border-color: #C53336;
    background-color: #FFF;
}
a.btn-main:active {
    color: #FFF;
    border-color: #C53336;
    background-color: #C53336;
}
a.disabled.btn-main {
    color: #999;
    border-color: #F5F5F5;
    background-color: #F5F5F5;
}
a.btn-weak {
    color: #333;
    border-color: #666;
    background-color: #FFF;
}
a.btn-weak:active {
    color: #FFF;
    border-color: #666;
    background-color: #666;
}
a.disabled.btn-weak {
    color: #999;
    border-color: #CCC;
    background-color: #FFF;
}

二、嵌套

嵌套可增强文件的结构性及可读性,但嵌套层级过多却是大忌,@at-root 可解决该问题。

@at-root

@at-root 可防止选择器的优先级过高。

先看一段很糟糕的代码,嵌套层级过多。层级关系的展现虽清晰,却莫名加高了选择器的 优先级。那么在重置样式的时候便需要加更高的优先级,由此恶性循环。

.parent {
    .child1 {
        width: 20px;
        .child1-1 {
            width: 20px; 
            .child1-1-1 {
                width: 20px;
            }
        }
    }
}

解析后的 css 代码:

.parent .child1 {
    width: 20px;
}
.parent .child1 .child1-1 {
    width: 20px;
}
.parent .child1 .child1-1 .child1-1-1 {
    width: 20px;
}

显然,.parent .child1 .child1-1 .child1-1-1 的优先级很高却完全没必要,我们需要做的便是降级。

.parent {
    .child1 {
        width: 20px;
        @at-root .child1-1 {
            width: 20px;
            @at-root .child1-1-1 {
                width: 20px;
            }
        }
    }
}

解析后的 css 代码:

.parent .child1 {
    width: 20px;
}
.child1-1 {
    width: 20px;
}
.child1-1-1 {
    width: 20px;
}

其实这并不是我想要的,我想要的是:

.parent .child1 {
    width: 20px;
}
.parent .child1-1 {
    width: 20px;
}
.parent .child1-1-1 {
    width: 20px;
}

显然 @at-root 无法控制跳出的级数,无奈。

@at-root 的另一应用场景便是 @keyframes。动画的专属性较强,嵌套的写法能清晰的标明其被应用之处。在做功能删除时只需直接干掉一整段代码,无需查找对应的 @keyframes 顺便思考其影响范围,省时高效。

示例代码:

.demo {
    animation: motion 1s infinite;
    @at-root {
        @keyframes motion {
            0% {background-color: red;}
            100% {background-color: yellow;}
        }
    }
}

解析后的 css 代码:

.demo {
    animation: motion 1s infinite;
}
@keyframes motion {
    0% {background-color: red;}
    100% {background-color: yellow;}
}

其实上述是 sass v3.3 之前的做法,自 sass v3.4 这样书写即可:

.demo {
    animation: motion 1s infinite;
    @keyframes motion {
        0% {background-color: red;}
        100% {background-color: yellow;}
    }
}

@keyframes 会自动跳出父级的限制。


三、继承

@mixin + @include

可传递参数,可设置参数默认值。

应用场景

相信大家都很反感在使用 transition、transform 这类 css3 属性时需要巴拉巴拉添加一堆前缀。

有了 sass 我们便可以定义一个自动添加前缀的方法啦!引用时,只需传入属性名及属性值,非常方便。且欲添加新的所须兼容的浏览器时,只需在 $compatibleBrowser 中添加配置项。若是使用传统做法,想象下某天老板要我们兼容 ms 了,你是要把全站代码修改一遍?

相关代码:

$compatibleBrowser: webkit, moz, o;
@mixin addPrefix ($property, $value) {
    @each $prefix in $compatibleBrowser {
        -#{$prefix}-#{$property}: $value;
    }
    #{$property}: $value;
}
.demo {
    background-color: red;
    @include addPrefix(transition, background-color 2s);
    &:hover {
        background-color: yellow;
    }
}

解析后的 css 代码:

.demo {
    background-color: red;
    -webkit-transition: background-color 2s;
    -moz-transition: background-color 2s;
    -o-transition: background-color 2s;
    transition: background-color 2s;
}
.demo:hover {
    background-color: yellow;
}

% + @extend

用于定义通用样式最为合适。

应用场景

通常,我们会创建这样一个文件 common.css 来定义一些通用样式,引用时在对应元素的 html 标签上添加相应的 class 即可。

而 sass 的做法如下:

%horizontal-scroll {
    white-space: nowrap;
    overflow-x: scroll;
    li {
        display: inline-block;
        vertical-align: middle;
    }
    &::-webkit-scrollbar {
        width: 0;
        height: 0;
        opacity: 0;
    }
}

section.horizontal-scroll {
    @extend %horizontal-scroll;
}

解析后的 css 代码:

section.horizontal-scroll {
    white-space: nowrap;
    overflow-x: scroll;
}
section.horizontal-scroll li {
    display: inline-block;
    vertical-align: middle;
}
section.horizontal-scroll::-webkit-scrollbar {
    width: 0;
    height: 0;
    opacity: 0;
}

这样做的优势为:

第一条优势显而易见,下面来为大家解释第二条:在代码被压缩上线时,传统做法会使得未被引用的通用样式一同被上线,而 sass 在被解析为 css 时会去除由 % 定义的未被引用的通用样式。


四、函数

产生值,非行为。

不要与 javascript 中的函数混淆。调用时并非执行了某段操作,而是返回了经过某些操作后产生的值,其实类似于有 return 值的 javascript 函数。


五、语句

三目判断

书写形式:

莫名觉得会很常用,与 javascript 中的 ? : 作用相同。

@if

应用场景

定义多行省略时(以 3 行为例),变更显示行数只需修改 line-clamp 属性值,代码如下:

.ellipsis-row3 {
    overflow: hidden;
    display: -webkit-box;
    text-overflow: -o-ellipsis-lastline;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}

而定义单行省略只需如下 3 行:

.ellipsis-row1 {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

两段代码差异较大,而我们又想将多行与单行的情况融合在一个通用样式内。此时,可以使用 if 语句将单行省略单独定义。

相关代码:

@for $lineCount from 1 through 3 {
    %ellipsis-row#{$lineCount} {
        overflow: hidden;
        @if $lineCount == 1 {
            white-space: nowrap;
            text-overflow: ellipsis;
        } @else {
            display: -webkit-box;
            text-overflow: -o-ellipsis-lastline;
            -webkit-box-orient: vertical;
            -webkit-line-clamp: $lineCount;
        }
    }
}

.section1 p {
    @extend %ellipsis-row1;
}
.section2 p {
    @extend %ellipsis-row2;
}
.section3 p {
    @extend %ellipsis-row3;
}

解析后的 css 代码:

.section1 p {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.section2 p {
    overflow: hidden;
    display: -webkit-box;
    text-overflow: -o-ellipsis-lastline;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}
.section3 p {
    overflow: hidden;
    display: -webkit-box;
    text-overflow: -o-ellipsis-lastline;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}

@for

for 循环的书写形式有两种:

第一种在循环遍历时会对第 end 项进行操作,第二种则不会。本人认为习惯使用一种即可,不然易混淆。

应用场景

我司喜好做 “固定模板” 需求:运营同学会将一张图片裁切成 n 个区域,点击每一区域的行为有所不同。比如一张美女的面部图,点击其眼睛时会推荐一些双眼皮项目,点击其鼻子时会推荐一些鼻综合项目等等(我司是做微整形交易平台的)。

区域划分是有一定规则的,如下图:当前行仅含一张图片时图片宽度为 100%,两张时为 50%,… n 张时为 (100 / n)%。

相关代码:

%clear-float {
    &:after {
        content: "";
        display: block;
        height: 0;
        clear: both;
        visibility: hidden;
    }
}
%adapt-image {
    display: block;
    width: 100%;
}
%static-template {
    div {
        @extend %clear-float;
        a {
            float: left;           
            img {
                @extend %adapt-image;
            }
        }
    }
    @for $itemCount from 1 through 3 {
        .count#{$itemCount} {
            a {
                width: 100%/$itemCount;
            }
        }
    }
}
section.static-template {
    @extend %static-template;
}

解析后的 css 代码:

section.static-template div:after {
    content: "";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}
section.static-template div a img {
    display: block;
    width: 100%;
}
section.static-template div a {
    float: left;
}
section.static-template .count1 a {
    width: 100%;
}
section.static-template .count2 a {
    width: 50%;
}
section.static-template .count3 a {
    width: 33.33333333%;
}

上述其实是 多列布局 的一个实例,多列布局 的应用很广泛,比如导航栏:

@each

each 可循环遍历两种数据类型:

具体实例请向上翻至 多值变量


2017/11/06 续更

Sass 安装

Sass 依赖于 Ruby 环境,若未安装 Ruby 请移驾此处下载。安装过程请勾选 Add Ruby executables to your PATH 确保添加环境变量。

若你具备翻墙技能请直接:

gem install sass

下面介绍如何不翻墙安装 Sass:

配置 gem sources

# 移除 https://rubygems.org/
gem sources --remove https://rubygems.org/

# 添加 https://gems.ruby-china.org/
gem sources -a https://gems.ruby-china.org/

# 查看 sources 确保仅有 https://gems.ruby-china.org/
gem sources -l

下载 sass.gem

下载地址

安装 sass.gem

gem install [sass.gem 路径]/sass.gem

作者:呆恋小喵

相关文章:通过 sass-resources-loader 全局注册 Sass 变量

我的后花园:https://sunmengyuan.github.io/garden/

我的 github:https://github.com/sunmengyuan

原文链接:https://sunmengyuan.github.io/garden/2017/05/17/sass-application.html