Sass 学习笔记

作者: 呆恋小喵 | 来源:发表于2017-05-18 15:52 被阅读32次

    本文以 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} 这样的书写方式,那就是所谓的特殊变量。

    多值变量

    • List 类型
    应用场景

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

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

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

    我们可以定义 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 类型
    应用场景

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

    我们可以将这些色值按照一定的规则储存在 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;
    }
    

    这样做的优势为:

    • css 样式与 html 结构解耦。

    • 去除冗余 css 代码。

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


    四、函数

    产生值,非行为。

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


    五、语句

    三目判断

    书写形式:

    • if(条件, 条件为真所取值, 条件为假所取值)

    莫名觉得会很常用,与 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 循环的书写形式有两种:

    • @for $var from start through end

    • @for $var from start to end

    第一种在循环遍历时会对第 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 可循环遍历两种数据类型:

    • @each $var in list

    • @each $var in map

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


    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

    相关文章

      网友评论

        本文标题:Sass 学习笔记

        本文链接:https://www.haomeiwen.com/subject/itbsxxtx.html