接触Sass差不多有一个年头了,在这一年来的时间中,也花了不少心思在Sass的学习上。同时也让自己喜欢上了Sass,目前在自己的私人项目中,我一直都在使用Sass做为前端开发,用来处理CSS。同时今年自己创建了下Sass中国网站来做Sass相关的技术分享。其实,在W3cplus站点上,已经发布了近一百篇有关于Sass方面的教程(教程有自己的学习心得、有译文,也有其他同学的使用经验分享)。也自认自己是Sass在中国的推广者,其实我也更想做为Sass在中国的布道者,让更多的人了解他,学习他,使用他。
那么回到话题的正题中来,今天要说的是Sass带来的变革,其实标题有点浮夸,也可以当其是标题党吧。他是出自于自己在兄弟公司做分享的一份PPT。因为时常能碰到同学在问:
- Sass是什么?
- 怎么学习Sass?
- 如何在项目中使用Sass?
等等一系列问题,其实在前面的教程中或多或少都有介绍,那么今天借此机会将前面所整理的教程、译文做一个系统的归纳吧,让有需要的同学能更好的学习和使用Sass。
Sass是什么?
Sass是"Syntactically Awesome StyleSheets"的简称。那么他是什么?其实没有必要太过于纠结,只要知道他是“CSS预处理器”中的一种即可,简单点说,Sass就是“CSS预处理器”,你可以称其是工具或者是语言。如果你实在想知道他是什么?可以看Sass官网上的一段描述。
Sass is an extension of CSS that adds power and elegance to the basic language. It allows you to use variables, nested rules, mixins, inline imports, and more, all with a fully CSS-compatible syntax. Sass helps keep large stylesheets well-organized, and get small stylesheets up and running quickly, particularly with the help of the Compass style library.
为什么选择Sass?
与Sass齐名的CSS预处理器还有LESS、Stylus等。这也引出一个激烈的讨论,Sass和LESS哪家技术强。而且这个讨论在很多论坛和平台都有出现过:比如2012年5月份Chris Coyier发表的《Sass vs LESS》一文,记得看文章后面的评论,因为评论的内容更精彩。
对于我个人为什么重新选择使用Sass,原因非常的简单,功能上来说他们都类似,也没有哪家比哪家强一说,但对于初学者,学习任何一门新技术,除了官网文档之外,希望有其他的教程或者资料,从这一点上来说,Sass相关的资料就非常的多。除此之外,使用Sass写的案例与框架也多。这就是我选择他的原因。如果你想先简单的了解Sass、LESS和Stylus有哪些不一样,再做出选择的话,可以看一篇2013年我在《程序员》杂志上发表的一篇文章《CSS预处理器——Sass、LESS和Stylus实践【未删减版】》。
扩展阅读
Sass功能与特性
Sass现在更新到3.4.7版本(写这篇文章时最新版本),其实Sass有很多功能和特性是CSS无法匹敌的,比如:变量、继承、混合宏、占位符、逻辑判断、函数、@at-root
、列表和map
等等。这些都是Sass的基本功能,可以说掌握了这些功能,配合你自己的创造力,可以使用Sass做更多有意义的事情。
扩展阅读
Sass学习路线
学习Sass还是具有一定的成本的。这样说吧,他和CSS基本类似,说得难听一点,你把你的.css
更换成.scss
文件,这就是现成的Sass。但要真正懂Sass还是需要一定的时间的。在这里,我来聊聊我是怎么样学习Sass的,或者说我学习Sass的一个路线是什么样的。
我将学习Sass分为三个阶段。
初级阶段
初级阶段就是一个入门的过程,知道怎么使用Sass。在这个过程中主要包括以下几个部分:
- 运行Sass环境
- Sass安装
- Sass语法
- Sass编译
- Sass调试
运行Sass的环境
Sass是基于Ruby开发的,所以要运行Sass都需要一个Ruby环境。但并不是说你要懂得Ruby,你只需要在你的电脑中安装一个Ruby环境即可。如果你使用的是Mac电脑,那么就不需要安装,如果你使用的是Win系统,那么需要先在电脑中安装Ruby。也正是因为这个原因,很多同学觉得Sass要依赖于Ruby环境,而放弃使用Sass。
至于如何安装Ruby,就不做过多阐述,因为现在的应用软件安装都是非常简单的,一路下一步即可。
扩展阅读
Sass安装
对于Sass安装来说是件非常简单的事情,只需要在你的命令终端输入一行命令即可:
gem install sass
提醒一下,在使用Mac的同学,可能需要在上面的命令加上sudo
,才能正常安装:
sudo gem install sass
如果你是一位Sass发烧友,你也可以通过--pre
参数来安装Sass开发版本,领略Sass的一些最新功能与特性:
gem install sass --pre
不过在天朝往往上面的命令让你无法正常实现安装,如果你碰到这样的事情,那么需要特殊去处理。可以到Rubygems网站上下载Sass安装包,然后在命令终端输入:
gem install <把下载的安装包拖到这里>
直接回车(Enter
)即可安装成功。如果你不确认你的Sass是否安装成功,只需要输入命令:
sass -v
看到版本号就表示安装成功。
Sass语法
Sass语法规则有两种,一种是通过tab
键控制缩进的语法规则(缩进要求非常严格),这种语法对于熟悉Ruby的同学来说会非常的方便和喜欢。这种语法的Sass文件是以.sass
后缀命名。另一种语法是SCSS,这是Sass的新的语法规则,他的外观和CSS的一模一样,文件后缀是.scss
。如下所示:
//Sass语法
$width: 200px
.box
width: $width
//SCSS语法
$width: 200px;
.box {
width: $width;
}
来看个示意图:
正因为如此,有不少同学,使用的是Sass新的语法规则,而文件后缀依旧是.sass
,这也就造成血案了,编译时说编译不出来。所以在此特别提醒新同学:.sass
只能使用Sass老语法规则(缩进规则),.scss
使用的是Sass新语法规则(类似CSS语法)。
上面只演示了最基础的语法规则,其实在定义混合宏,调用混合宏,他们都略有不同。对于前端人员,个人更建议使用SCSS语法风格,比较适应,也不会那么容易出错。
Sass编译
众所周知,到目前为止,各浏览器是无法直接解析.scss
或者.sass
文件。换句话说,在Web实际掉用当中,还是需要调用.css
文件。这个问题也困扰了很多初学者,常常有人会问,使用Sass进行开发,那么是不是直接通过<link>
引用.scss
或.sass
文件呢?那么这里告诉大家,在项目中还是引用.css
文件,Sass只不过是做为一个预处理工具,提前帮你做事情,只有你需要的时候,他才能功效。
这样一来,在Sass开发之后,要使用写好的东西,让Web页面能调用,就得经过一个过程,这个过程就是Sass编译过程。Sass的编译有多种方法:
命令编译
如果你喜欢操纵你的命令终端,那么可以直接通过命令终端来对Sass进行编译,只需要命令终端输入:
sass <要编译的Sass文件路径>/style.scss:<要输出CSS文件路径>/style.css
这是对一个单文件进行编译,如果想对整个项目里所有Sass文件编译成CSS文件,可以这样操作:
sass sass/:css/
上面的命令表示将项目中sass
目录中所有.scss
(.sass
)文件编译成.css
文件,并且这些CSS文件都放在css
目录当中。
在实际编译过程中,你会发现上面的命令,只能一次性编译。每次修改保存.scss
文件之后,都得得新执行一次这样的命令,如此操作太麻烦,其实还有一种方法,就是在编译Sass时,开启watch
功能,这样只要你的代码进行任何修改,他都能自动监测到代码的变化,并且给你直接编译过来。
sass --watch <要编译的Sass文件路径>/style.scss:<要输出CSS文件路径>/style.css
命令编译就是这么的简单。当然,使用sass
命令编译时,可以带很多参数。
GUI编译
如果平时工作中不太喜欢使用命令终端的同学,可以考虑使用GUI界面工具来对Sass进行编译。当然不同的GUI工具操作方法略有不同。在此也不一一对编译的界面工具做详细的介绍。对于GUI界面编译工具,目前较为流行的主要有:
自动化配置编译Sass
喜欢自动化研究的同学,应该都知道Grunt和Gulp这两个东东。如果您正在使用其中的任何一种,那么你也可以通过他们来配置,也可以完成Sass的编译。
//Grunt
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
dist: {
files: {
'style/style.css' : 'sass/style.scss'
}
}
},
watch: {
css: {
files: '**/*.scss',
tasks: ['sass']
}
}
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default',['watch']);
}
//Gulp
var gulp = require('gulp');
var sass = require('gulp-sass');
gulp.task('sass', function () {
gulp.src('./scss/*.scss')
.pipe(sass())
.pipe(gulp.dest('./css'));
});
gulp.task('watch', function() {
gulp.watch('scss/*.scss', ['sass']);
});
gulp.task('default', ['sass','watch']);
扩展阅读
Sass调试
Sass调试一直以来都是一件头痛的事情,使用Sass的同学都希望能在浏览器中直接调试Sass文件,能找到对应的行数。值得庆幸的是,现在要实现并不是一件难事了,只要你的浏览器支持"Sourcemap"功能即可。早一点的版本,需要在编译的时候添加--sourcemap
参数:
sass --watch --scss --sourcemap style.scss:style.css
在3.3版本之上(我测试使用的版本是3.4.7),不需要添加这个参数也可以:
sass --watch style.scss:style.css
在命令终端,你将看到一个信息:
>>> Change detected to: style.scss
write style.css
write style.css.map
这时你就可以像前面展示的gif图一样,调试你的Sass代码。
扩展阅读
如果掌握了上面提到的知识,我想你已具备Sass的初级水平。你会安装Sass、知道Sass语法、会编写Sass代码,也能编译Sass,还能调试Sass代码。但这仅仅是Sass的基础知识。如果还要深入,还是需要花不少时间去学习与实战的。
中级阶段
具有Sass初级阶段水平之后,你对Sass也有了基本的了解,也能用Sass去做一些简单的事情。如果要深入还是要继续往下学习的。接下来向大家简单介绍有关于Sass方面更有兴趣的东东。
Sass的基本功能
Sass功能和特性有很多,要把所有东西介绍完,我想完全可以写一本书了,那么在这里主要向大家介绍一些Sass最基本、最常用的特性:
- 变量
- 混合宏
@mixin
- 继承
- 占位符
%placeholder
- 嵌套
- 运算符
- 选择符
&
- 列表
$list
- 函数
@function
- map
- 控制命令
@at-root
变量
先来看一张图
上图非常清楚告诉了大家,Sass的变量包括三个部分:
- 声明变量的符号
$
- 变量名称
- 赋予变量的值
定义变量,就可以在代码中调用了:
//SCSS
$color: orange !default;
.block {
color: $color;
}
//CSS
.block {
color: orange;
}
说到变量,大多数人都会想到全局变量和局部变量。早期的Sass是不具有这样的概念,但新版本中3.4之后有点这方面的意思了。来看一个简单的示例:
//SCSS
$color: orange !default;
.block {
color: $color;
}
em {
$color: red;
a {
color: $color;
}
}
span {
color: $color;
}
//CSS
.block {
color: orange;
}
em a {
color: red;
}
span {
color: orange;
}
上面的示例演示可以得知,在元素内部定义的变量不会影响其他元素。如此可以简单的理解成,定义在元素外面的变量,比如$color:orange !default;
是一个全局变量,而定义在元素内部的变量,比如$color:red;
是一个局部变量。除此之外,Sass现在还提供一个!global
参数:
//SCSS
$color: orange !global;
.block {
color: $color;
}
em {
$color: red;
a {
color: $color;
}
}
span {
color: $color;
}
//CSS
.block {
color: orange;
}
em a {
color: red;
}
span {
color: orange;
}
!global
从名称上看是一个合局变量,但和全面似乎没有太大区别,不过我们来换过一种测试效果,将!gobal
放在内部,其也将会影响全局:
//SCSS
$color: orange !global;
.block {
color: $color;
}
em {
$color: red !global;
a {
$color: lime;
color: $color;
}
}
span {
$color: yellow;
color: $color;
}
.i {
color: $color;
}
//CSS
.block {
color: orange;
}
em a {
color: lime;
}
span {
color: yellow;
}
.i {
color: red;
}
除非元素自身重置变量,才能覆盖!global
的变量。
扩展阅读
混合宏@mixin
Sass中的混合宏是通过@mixin
来声明,然后通过@include
来引用。混合宏主要功能就是将一些共用功能样式代码合并在一起。比如:
@mixin box-shadow($shadow...) {
@if length($shadow) >= 1 {
box-shadow:$shadow;
} @else{
$shadow:0 0 4px rgba(0,0,0,.3);
box-shadow:$shadow;
}
}
引用混合宏是通过@include
:
.block {
@include box-shadow;
}
.hidden {
@include box-shadow(inset 0 0 1px rgba(red,.5),0 0 2px rgba(red,.25));
}
编译出来的CSS:
.block {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
}
.hidden {
box-shadow: inset 0 0 1px rgba(255, 0, 0, 0.5), 0 0 2px rgba(255, 0, 0, 0.25);
}
这样可能还看不出其特色,将上面的示例稍作变化:
//SCSS
.block {
@include box-shadow;
}
.hidden {
@include box-shadow(inset 0 0 1px rgba(red,.5),0 0 2px rgba(red,.25));
}
.header {
@include box-shadow;
h2 {
@include box-shadow(1px 1px 2px rgba(green,.3),inset 0 0 2px rgba(green,.3));
}
}
//CSS
.block {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
}
.hidden {
box-shadow: inset 0 0 1px rgba(255, 0, 0, 0.5), 0 0 2px rgba(255, 0, 0, 0.25);
}
.header {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
}
.header h2 {
box-shadow: 1px 1px 2px rgba(0, 128, 0, 0.3), inset 0 0 2px rgba(0, 128, 0, 0.3);
}
编译出来的CSS代码,大家不难发现,其中.block
和.header
是样式代码是一样的,但Sass编译出来并没有将其合并在一起:
.block {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
}
...
.header {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
}
这其实也是Sass中混合宏@mixin
最不尽人意之处,其最大特征就是可以为其传参数。那么大家需要记住一点,如果你的功用代码块,需要传参数,那么这个功能块应该使用Sass的混合宏@mixin
来定义。
扩展阅读
- 理解SASS的嵌套,
@extend
,%Placeholders
和Mixins - sass揭秘之
@mixin
,%
,@function
- Sass中半透明颜色的Mixins
- Sass基础——PX to EM Mixin和
@function
- SASS基础——SASS Triangle Mixin
- SASS基础——十个常见的Mixins
- Sass Mixins——支持Retina的Icons Sprite
- 用Sass的占位符和混合宏创建可复用的样式
- 十个有用的Sass Mixins
继承
继承对于CSS来说并不是陌生的事情,先来看张图:
图中代码显示.col-sub .block li,.col-extra .block li
继承了.item-list ul li
选择器的padding:0;
和ul li
选择器中的list-style:none outside none;
以及*
选择器中的box-sizing:inherit;
。在Sass中也具有继承一说,也是继承类中的样式代码块。在Sass中通过@extend
来实现代码块的继承,如下所示:
//SCSS
.btn {
border: 1px solid #ccc;
padding: 6px 10px;
font-size: 14px;
}
.btn-primary {
background-color: #f36;
color: #fff;
@extend .btn;
}
.btn-second {
background-clor: orange;
color: #fff;
@extend .btn;
}
//CSS
.btn, .btn-primary, .btn-second {
border: 1px solid #ccc;
padding: 6px 10px;
font-size: 14px;
}
.btn-primary {
background-color: #f36;
color: #fff;
}
.btn-second {
background-clor: orange;
color: #fff;
}
从示例代码可以看出,在Sass中的继承也可以继承类中所有样式代码,而且编译出来的CSS会将选择器合并在一起,形成组合选择器:
.btn, .btn-primary, .btn-second {
border: 1px solid #ccc;
padding: 6px 10px;
font-size: 14px;
}
占位符%placeholder
Sass中的占位符%placeholder
功能是一个很强大,很实用的一个功能,这也是我非常喜欢的功能。他可以取代以前CSS中的基类造成的代码冗余的情形。因为%placeholder
声明的代码,如果不被@extend
调用的话,不会产生任何代码。来看一个演示:
%mt5 {
margin-top: 5px;
}
%pt5{
padding-top: 5px;
}
这段代码没有被@extend
调用,他并没有产生任何代码块,只是静静的躺在你的某个SCSS文件中。只有通过@extend
调用才会产生代码:
//SCSS
%mt5 {
margin-top: 5px;
}
%pt5{
padding-top: 5px;
}
.btn {
@extend %mt5;
@extend %pt5;
}
.block {
@extend %mt5;
span {
@extend %pt5;
}
}
//CSS
.btn, .block {
margin-top: 5px;
}
.btn, .block span {
padding-top: 5px;
}
从编译出来的CSS代码可以看出,通过@extend
调用的占位符,编译出来的代码会将相同的代码合并在一起。这也是我们希望看到的效果,也让你的代码变得更为干净。
扩展阅读
混合宏 VS 继承 VS 占位符
初学者都常常纠结于这个问题“什么时候用混合宏,什么时候用继承,什么时候使用占位符?”其实他们更有更的优点与缺点,先来看看他们使用效果:
//SCSS中混合宏使用
@mixin mt($var){
margin-top: $var;
}
.block {
@include mt(5px);
span {
display:block;
@include mt(5px);
}
}
.header {
color: orange;
@include mt(5px);
span{
display:block;
@include mt(5px);
}
}
//CSS
.block {
margin-top: 5px;
}
.block span {
display: block;
margin-top: 5px;
}
.header {
color: orange;
margin-top: 5px;
}
.header span {
display: block;
margin-top: 5px;
}
编译出来的CSS清晰告诉了大家,他不会自动合并相同的样式代码,如果在样式文件中调用同一个混合宏,会产生多个对应的样式代码,造成代码的冗余,这也是CSSer无法忍受的一件事情。不过他并不是一无事处,他可以传参数。个人建议:如果你的代码块中涉及到变量,建议使用混合宏来创建相同的代码块。
同样的,将上面代码中的混合宏,使用类名来表示,然后通过继承来调用:
//SCSS 继承的运用
.mt{
margin-top: 5px;
}
.block {
@extend .mt;
span {
display:block;
@extend .mt;
}
}
.header {
color: orange;
@extend .mt;
span{
display:block;
@extend .mt;
}
}
//CSS
.mt, .block, .block span, .header, .header span {
margin-top: 5px;
}
.block span {
display: block;
}
.header {
color: orange;
}
.header span {
display: block;
}
使用继承后,编译出来的CSS会将使用继承的代码块合并到一起,通过组合选择器的方式向大家展现,比如.mt, .block, .block span, .header, .header span
。这样编译出来的代码相对于混合宏来说要干净的多,也是CSSer期望看到。但是他不能传变量参数。个人建议:如果你的代码块不需要专任何变量参数,而且有一个基类已在文件中存在,那么建议使用Sass的继承。
最后来看占位符,将上面代码中的基类.mt
换成Sass的占位符格式:
//SCSS中占位符的使用
%mt{
margin-top: 5px;
}
.block {
@extend %mt;
span {
display:block;
@extend %mt;
}
}
.header {
color: orange;
@extend %mt;
span{
display:block;
@extend %mt;
}
}
//CSS
.block, .block span, .header, .header span {
margin-top: 5px;
}
.block span {
display: block;
}
.header {
color: orange;
}
.header span {
display: block;
}
编译出来的CSS代码和使用继承基本上是相同,只是不会在代码中生成占位符mt
的选择器。那么占位符和继承的主要区别的,“占位符是独立定义,不调用的时候是不会在CSS中产生任何代码;继承是首先有一个基类存在,不管调用与不调用,基类的样式都将会出现在编译出来的CSS代码中。”
通过对比,总结一下:
- 混合宏@mixin
:如果相同代码块需要在不同的环境传递不同的值时,可以通过混合宏来定义重复使用的代码块,其不足之处就是编译出来的CSS代码什么多次出现调用的混合宏对应的代码块,使用文件变得臃肿,代码冗余。
- 继承:如果相同代码块不需要传递不同的值,并且此代码块已在Sass文件中定义,此进可以通过Sass的继承来调用已存在的基类。使用继承将会将调用相同基类的代码合并在一起。不足之处时,如果基类,并不存在于HTML结构时,不管调用与不调用,在编译出来的CSS中都将产生基类对应的样式代码。
- 占位符%placeholder
:占位和继承基本类似,唯一不同的是,相同代码块并没有在基类中存中,而是额外声明。如果不调用已声明的占位符,将不会产生任何样式代码,如果在不同选择器调用占位符,那么编译出来的CSS代码将会把相同的代码合并在一起。
扩展阅读
单类 VS 多类
在这种情形之下,引出一个新的争论点,那就是单类与多类的火拼。在实际项目中应该使用哪种方式。对于这样的争论一不是一时半刻的事情,其实他们一直都还在争论,而倒底应该使用单类还是多类,到目前也还没有结果。个人觉得还是根据实际情况出发吧。如果您对这方面的讨论和相关知识点感兴趣的话,不仿看看下面相关文章:
- Single Class vs. Multi Class CSS
- Multiple classes for UI component variations – are we doing it wrong?
- About HTML semantics and front-end architecture
- Challenging CSS Best Practices
- Modular CSS with Sass & BEM
- Modular CSS: Thoughts on SMACSS modules
- BEM修饰符:多类名 VS Sass @extend
我们回过来头来细想,使用单类,在CSS中可能会造成代码的冗余,难于维护;而使用多类更易维护,而且代码更干净。但单类在HTML中更具语法义,也无需过多去维护HTML;如果使用多类,在HTML中语义化规划更具难度,对于结构也更难维护。其实将Sass结合在一起,将不会这么纠结。早前在写CSS的时候很多同学喜欢这样写:
//CSS
.paxs{padding:5px;}
.pas {padding: 10px;}
.pam{padding:15px;}
.pal{padding:20px;}
.paxl{padding: 25px;}
.paxxl{padding: 30px;}
.maxs{margin:5px;}
.mas {margin: 10px;}
.mam{margin:15px;}
.mal{margin:20px;}
.maxl{margin: 25px;}
.maxxl{margin: 30px;}
你的项目中可能会有一个基类文件,比如common.css
,里面放了很多类似于上面的代码,然后在结构中通过多类引用:
//HTML
<div class="header maxs paxs"></div>
虽然这种方式解决了你的需求,但如果你的margin
或padding
值修改后,你需要同时修改你的HTML代码,或者在CSS中通过覆盖的方式来完成。但在Sass中,我们可以这样使用:
//SCSS中定义%placeholder,可以将这一部分代码放在_help.scss中
%paxs{padding:5px;}
%pas {padding: 10px;}
%pam{padding:15px;}
%pal{padding:20px;}
%paxl{padding: 25px;}
%paxxl{padding: 30px;}
%maxs{margin:5px;}
%mas {margin: 10px;}
%mam{margin:15px;}
%mal{margin:20px;}
%maxl{margin: 25px;}
%maxxl{margin: 30px;}
//实际中引用
.header {
@extend %paxs;
@extend %maxs;
}
//CSS
.header {
padding:5px;
margin:5px;
}
在HTML中,只需要引用单类:
//HTML
<div class="header"></div>
这种情形之下,就算你要修改margin
或padding
值,你也只需要通过修改.scss
文件,无需修改任何HTML文件中代码即可。
相比之下,在实际中如何使用,或者你将选择单类还是多类方式,可以权衡其利弊。
嵌套
Sass中还提供了选择器嵌套功能,但这也并不意味着你在Sass中的嵌套是无节制的,因为你嵌套的层级越深,编译出来的CSS代码,选择器层级将越深,这往往是大家不愿意看到的一点。比如:
//SCSS
.block {
color: green;
span {
color: blue;
a {
color: orange;
i{
color: lime;
em &{
color: red;
}
}
}
}
}
//CSS
.block {
color: green;
}
.block span {
color: blue;
}
.block span a {
color: orange;
}
.block span a i {
color: lime;
}
em .block span a i {
color: red;
}
如此一来,在编写Sass代码时,使用选择器嵌套还是需要遵循一定的原则,其中最关键之处:别让你的嵌套层级超过四层。
//SCSS
.block {
color: green;
span {
color: blue;
}
a {
color: orange;
}
i{
color: lime;
em &{
color: red;
}
}
}
//CSS
.block {
color: green;
}
.block span {
color: blue;
}
.block a {
color: orange;
}
.block i {
color: lime;
}
em .block i {
color: red;
}
扩展阅读
运算符
Sass还提供了一些运算符,可以在代码中做一些简单的计算,其中包括+
、-
、*
和/
等。
//SCSS
$hello: hello;
$world: world;
$width: 200px;
.string {
sting: $hello + $world;
}
body {
width: $width * 2;
}
//CSS
.string {
sting: helloworld;
}
body {
width: 400px;
}
使用Sass运算符做一些运算时,在运算符前后要留有空格。其中特别要注意的是
font
属性中font-size
与line-height
简写时,其中/
分隔线与运算符中的除号/
相同,不管你是这样写:
//SCSS
$font-size: 2em;
$line-height: 1.5;
$font-family: "Arial";
body {
font: $font-size / $line-height $font-family;
}
//CSS
body {
font: 1.3333333333em "Arial";
}
还是这样写:
//SCSS
$font-size: 2em;
$line-height: 1.5;
$font-family: "Arial";
body {
font: $font-size/$line-height $font-family;
}
//CSS
body {
font: 1.33333em "Arial";
}
都将出错。此时你需要使用Sass中的插值#{}
:
//SCSS
$font-size: 2em;
$line-height: 1.5;
$font-family: "Arial";
body {
font: $font-size/#{$line-height} $font-family;
}
//CSS
body {
font: 2em/1.5 "Arial";
}
同样的道理,在CSS属性中碰到缩写属性带有/
符号的,在编写Sass代码时都需使用插值#{}
,以免造成不必要的编译错误。
选择符&
连体符&
在Sass中也是一个很有意思,一个奇特的东东。特别是在选择器的嵌套,BEM+Sass的运用中,其发挥着与众不同的功能。看代码中的演示,将会来得实际一些:
//SCSS
.block {
color: green;
&:after {
content:"";
display:table;
}
#{&}__element{
color:orange;
}
#{&}--modify{
color:blue;
}
&.info {
color: lime;
}
.page &{
color: yellow;
}
}
//CSS
.block {
color: green;
}
.block:after {
content: "";
display: table;
}
.block .block__element {
color: orange;
}
.block .block--modify {
color: blue;
}
.block.info {
color: lime;
}
.page .block {
color: yellow;
}
&
的奥妙尽在代码之中。其实你变换他的位置,简单点说吧,配合选择器嵌套,将会产生更多种不同组合效果,上面代码演示的是最常见的效果。如果您感兴趣,可以自己探索探索。
扩展阅读
列表$list
Sass中的List是一个让人可爱又可恨的东西。主要是他的语法太宽松,你几乎可以做任何你想做的事情。如果要想更好的使用好Sass语言中的List功能,我们就必须的深入了解他。在这里无法详细的阐述Sass中的List是怎么一回事,我们来看点简单的。
在Sass中,声明List和声明变量非常的相似,而且其声明的方式方法也是多样,主要因为其语法非常宽松,如:
//定义变量
$list:();//定义一个空的列表
$list:(#b4d455,42,"awesome");
$list-space: "item-1""item-2""item-3";
//定义一个多维列表
$list: (
("item-1.1", "item-1.2", "item-1.3"),
("item-2.1", "item-2.2", "item-2.3"),
("item-3.1", "item-3.2", "item-3.3")
);
在实际运用中,Sass的列表配合控制命令@each
、@for
之类,或者函数nth()
可以做很多事情,来看一个简单示例:
//SCSS
$list:(#b4d455,42,"awesome");
body {
color: nth($list,1);
font-size: nth($list,2) * length($list) + px;
font-family: nth($list,3);
}
//CSS
body {
color: #b4d455;
font-size: 126px;
font-family: "awesome";
}
扩展阅读
函数@function
在Sass中除了可以定义变量,具有@extend,%placeholders和Mixins等特性之外,还自备了一系列的函数功能。其主要包括字符串函数、数字函数、列表函数、Introspection函数以及三元函数等。
字符串函数
- unquote($string):删除字符串中的引号;
- quote($string):给字符串添加引号。
数字函数
- percentage($value):将一个不带单位的数转换成百分比值;
- round($value):将数值四舍五入,转换成一个最接近的整数;
- ceil($value):将大于自己的小数转换成下一位整数;
- floor($value):将一个数去除他的小数部分;
- abs($value):返回一个数的绝对值;
- min($numbers…):找出几个数值之间的最小值;
- max($numbers…):找出几个数值之间的最大值。
列表函数
- length($list):返回一个列表的长度值;
- nth($list, $n):返回一个列表中指定的某个标签值
- join($list1, $list2, [$separator]):将两个列给连接在一起,变成一个列表;
- append($list1, $val, [$separator]):将某个值放在列表的最后;
- zip($lists…):将几个列表结合成一个多维的列表;
- index($list, $value):返回一个值在列表中的位置值。
Introspection函数
- type-of($value):返回一个值的类型
- unit($number):返回一个值的单位;
- unitless($number):判断一个值是否带有带位
- comparable($number-1, $number-2):判断两个值是否可以做加、减和合并
Miscellaneous函数
在这里把Miscellaneous函数称为三元条件函数,主要因为他和JavaScript中的三元判断非常的相似。他有两个值,当条件成立返回一种值,当条件不成立时返回另一种值:
if($condition,$if-true,$if-false)
上面表达式的意思是当$condition
条件成立时,返回的值为$if-true
,否则返回的是$if-false
值。
除了这些函数之外,Sass还有颜色函数,以及后面新增的Maps函数和选择器函数。有关于Sass所有自带函数的使用,还可以查看官网的函数列表。
自定义函数
很多时候,Sass自带的函数是无法满足业务的需求,这个时候,用户还可以根据自己的需求定义函数。如:将px
转换成rem
:
//SCSS
@function pxTorem($px,$browser-default-font-size){
@return $px / $browser-default-font-size * 1rem;
}
h2 {
font-size: pxTorem(32px,16px);
}
//CSS
h2 {
font-size: 2rem;
}
扩展阅读
- Sass基础——Sass函数
- Sass基础——颜色函数
- Sass基础——PX to EM Mixin和
@function
- Sass揭秘之
@mixin
,%
,@function
- Sass函数功能——
rem
转px
- 【Sass高级】Sass中的反三角函数
map
Sass3.3新增了一个功能特性,那就是map
。它可以帮助更好的组织Sass代码。从外形上看map
长得有点类似于$list
,其实你可以将其理解成类似其他语言中的数组,或JSON
。在此就不做过多纠结,来简单看看他长得样子:
$map: (
key: value,
other-key: other-value
);
复杂一点的,可以map
里面套map
(记得前面的$list
也可以是多层$lsit
)。
// _config.scss
$breakpoints: (
small: 767px,
medium: 992px,
large: 1200px
);
// _mixins.scss
@mixin respond-to($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: #{map-get($breakpoints, $breakpoint)}) {
@content;
}
}
@else {
@warn "Unfortunately, no value could be retrieved from `#{$breakpoint}`. "
+ "Please make sure it is defined in `$breakpoints` map.";
}
}
// _component.scss
.element {
color: hotpink;
@include respond-to(small) {
color: tomato;
}
}
上面这个就是使用了map
管理断点的一个示例,其编译出来的代码:
.element {
color: hotpink;
}
@media (min-width: 767px) {
.element {
color: tomato;
}
}
Sass中的map
可以做的事情非常的多,特别借助Sass提供的map
函数功能,能帮你做更多更有意义的事情,除此之外,你还可以开发一些适合自己业务需求的函数。
需要特别声明的是,Sass中的map
有前面介绍的"Sourcemap"可不是同一个东东,千万别混淆了。
扩展阅读
- 探索Sass3.3中的Maps(一)
- 探索Sass3.3中的Maps(二):Sass Maps和Memoization
- Sass Maps
- 使用list-maps将你的Sass技术水平提高到另一层次
- 使用Sass Maps
控制命令
Sass中控制命令指的是@if
、@each
、@for
和@while
。具有一定的逻辑判断和循环遍历能力,这个对于懂JavaScript或者后端语言的同学来说一点都不难。对在CSS中是不可思议的一件事情,最起码到目前为止是不太可能的事情。但在Sass这样的CSS预处理器语言中实现了(当然,在LESS和Stylus中也具备这方面功能)。
@if
@if
是一个条件判断语句,简单点的就是如果条件成立,处理什么,反之条件不成立处理什么?在Sass中除了@if
之外,还可以配合@else
一起使用:
//SCSS
@mixin blockOrHidden($boolean:true) {
@if $boolean {
@debug "$boolean is #{$boolean}";
display: block;
}
@else {
@debug "$boolean is #{$boolean}";
display: none;
}
}
.block {
@include blockOrHidden;
}
.hidden{
@include blockOrHidden(false);
}
//CSS
.block {
display: block;
}
.hidden {
display: none;
}
@each
@each
是用来做遍历的,在Sass中$list
中的值要一个一个输出,那么就可以使用@each
命令来遍历输出。简单点,他们里面有一定的规律,让其按一定的规律输出。
//SCSS
$socials: twitter facebook twitter google rss email;
@mixin icon-socials {
@each $social in $socials {
.iocn-#{$social} {
background: url("../images/#{$social}.png") no-repeat;
}
}
}
@include icon-socials;
//CSS
.iocn-twitter {
background: url("../images/twitter.png") no-repeat;
}
.iocn-facebook {
background: url("../images/facebook.png") no-repeat;
}
.iocn-twitter {
background: url("../images/twitter.png") no-repeat;
}
.iocn-google {
background: url("../images/google.png") no-repeat;
}
.iocn-rss {
background: url("../images/rss.png") no-repeat;
}
.iocn-email {
background: url("../images/email.png") no-repeat;
}
这个在Icon的运行中特别实用,上面演示的还是简单的一种,其实还可以更复杂一些:
//SCSS
$socials: twitter facebook twitter google rss email;
@mixin icon-socials {
@each $social in $socials {
.iocn-#{$social} {
background: url("../images/icon.png") no-repeat 0 (-(index($socials,$social)) * 60px);
}
}
}
@include icon-socials;
//CSS
.iocn-twitter {
background: url("../images/icon.png") no-repeat 0 -60px;
}
.iocn-facebook {
background: url("../images/icon.png") no-repeat 0 -120px;
}
.iocn-twitter {
background: url("../images/icon.png") no-repeat 0 -60px;
}
.iocn-google {
background: url("../images/icon.png") no-repeat 0 -240px;
}
.iocn-rss {
background: url("../images/icon.png") no-repeat 0 -300px;
}
.iocn-email {
background: url("../images/icon.png") no-repeat 0 -360px;
}
@for
Sass中的@for
是一种循环遍历。他有两种方式:
@for $var from <start> through <end>
@for $var from <start> to <end>
其功能都是类似,只是截止的点不一同。来看一个对比示例:
//SCSS
$grid-prefix: span !default;
$grid-width: 60px !default;
$grid-gutter: 20px !default;
%grid {
float: left;
margin-left: $grid-gutter / 2;
margin-right: $grid-gutter / 2;
}
@for $i from 1 through 12 {
.#{$grid-prefix}#{$i}{
width: $grid-width * $i + $grid-gutter * ($i - 1);
@extend %grid;
}
}
将上面的代码稍做修改,将@for through
方式换成@for to
:
//SCSS
@for $i from 1 to 13 {
.#{$grid-prefix}#{$i}{
width: $grid-width * $i + $grid-gutter * ($i - 1);
@extend %grid;
}
}
这两种方式编译出来的结果:
.span1, .span2, .span3, .span4, .span5, .span6, .span7, .span8, .span9, .span10, .span11, .span12 {
float: left;
margin-left: 10px;
margin-right: 10px;
}
.span1 {
width: 60px;
}
.span2 {
width: 140px;
}
.span3 {
width: 220px;
}
.span4 {
width: 300px;
}
.span5 {
width: 380px;
}
.span6 {
width: 460px;
}
.span7 {
width: 540px;
}
.span8 {
width: 620px;
}
.span9 {
width: 700px;
}
.span10 {
width: 780px;
}
.span11 {
width: 860px;
}
.span12 {
width: 940px;
}
这两段Sass代码并无太多差别,只是@for
中的<end>
取值不同。配合through
的<end>
值是12
,其遍历出来的终点值也是12
,和<end>
值一样。配合to
的<end>
值是13
,其遍历出来的终点值是12
,就是<end>
对就的值减去1
。
@while
@while
也可以做到遍历的效果,比如:
//SCSS
$types: 4;
$type-width: 20px;
@while $types > 0 {
.while-#{$types} {
width: $type-width + $types;
}
$types: $types - 1;
}
//CSS
.while-4 {
width: 24px;
}
.while-3 {
width: 23px;
}
.while-2 {
width: 22px;
}
.while-1 {
width: 21px;
}
在Sass中,@if
、@each
、@for
和@while
这些控制命令配合@mixin
和@function
可以实现一些复杂的功能。有兴趣的同学可以去尝试一下。
扩展阅读
@at-root
@at-root
从字面说就是在根上。其实@at-root
早期是为BEM而生的,比如《Sass @at-root》一文中所介绍的。不过新的Sass对这个功能做出了调整。
在介绍选择器嵌套时,为了避免编译出来的CSS选择器不会因为Sass中嵌套过深而层级过多,在编写Sass代码时,尽量让嵌套层级不要超过三层。但往往使用嵌套都是为了更好的将代码按功能块来写。Sass考虑到能让开发员快速将编写的代码跳出层级限制,而又不使用编译出来的代码选择器冗余。将@at-root
用于此处。来看一个示例:
//SCSS
.block {
color: green;
ul {
list-style:none outside none;
li {
margin:0;
@at-root a {
color:green;
}
}
}
}
//CSS
.block {
color: green;
}
.block ul {
list-style: none outside none;
}
.block ul li {
margin: 0;
}
a {
color: green;
}
编译出来的a
直接跳出去了。但这也不是大家想要的,如果不想完全跳到最外面,那么@at-root
又歇菜了,不过稍加改良一下,就OK。看两个@mixin
:
//SCSS
@mixin parent-nth-status($index, $status, $place: suffix) {
$func-name: parent-nth-status;
$new-selectors: ();
$selectors: &;
$status: unquote($status);
$place: unquote($place);
@if not $selectors {
@error "#{$func-name} should not at root!";
}
@if not $status or $status == "" {
@error "#{$func-name} parameter $status error";
}
@each $selector in $selectors {
$len: length($selector);
$index: if($index < 0, $len + $index + 1, $index);
$result: ();
@for $i from 1 through $len {
$item: nth($selector, $i);
@if $i == $index {
@if $place == suffix {
$result: $item + $status;
} @else if $place == prefix {
$result: $status + $item;
} @else if $place == prepend {
$result: append($status, $item);
} @else if $place == append {
$result: append($item, $status);
}
}
@else {
$result: append($result, $item);
}
}
$new-selectors: append($new-selectors, $result, comma);
}
@at-root #{$new-selectors} {
@content;
}
}
@mixin parent-status($status, $place: suffix) {
@include parent-nth-status(-2, $status, $place) {
@content;
}
}
.tab {
a {
display: inline-block;
padding: 10px 60px;
cursor: pointer;
&:hover {
background: #AAA;
}
i {
margin-left: 10px;
@include parent-status(':hover') { color: red; }
@include parent-status('.home-link') { background: blue; }
@include parent-status('.about-link') { background: green; }
}
}
}
//CSS
.tab a {
display: inline-block;
padding: 10px 60px;
cursor: pointer;
}
.tab a:hover {
background: #AAA;
}
.tab a i {
margin-left: 10px;
}
a:hover i {
color: red;
}
a.home-link i {
background: blue;
}
a.about-link i {
background: green;
}
是不是让你觉得一下爽了。
如何在项目中使用Sass?
很多时候,大家更为关心的如何在项目中使用Sass。其实这是一个很简单的问题,只是从未动手过而以。这里说说是怎么将Sass和项目结合在一起。
在平时做项目的时候,项目中都会包括几个常用的文件目录:
- css:主要放置
.css
文件 - js:主要放置
.js
文件 - images:主要放置图片文件
- html:主要放置
.html
或.php
之类文件(我一般喜欢直接放在项目根录下)
而Sass用到项目中并和上面无太多的差别,只需要在项目中创建一个sass
目录,好放置.scss
或者.sass
文件。前面也说过了,最终在Web调用的文件是编译出来的.css
文件。在没有一定经验的时候,可以将所有的.scss
文件放在sass
目录中。你也可以将其细分出来,因为很多代码不只用于一个项目,可以用到其他项目当中,我常常是这样组织我的.scss
文件:
项目组织结构:
sass目录结构:
style.scss
文件引用:
这是一个简单的项目,其实喜欢Sass的同学,可以看看其他项目的是如何组织相关文件,比如说Foundation、Bootstrap和Compass等。
扩展阅读
OOSass
OOSass其实就是将OOCSS和Sass两者结合在一起的产物。虽然OOCSS给我们写样式带来重大的变革,但依旧是痛楚不断。其实将OOCSS和Sass结合在一起,你将不再那么痛苦。在此,用一个简单的示例来演示整个过程。
下面有两很拙的按钮:
不能再拙的按钮了,咱也不纠结了。记得当初刚接触这个行业时,我傻傻这样做:
HTML
<a href="#" class="twitter">Twitter</a>
<a href="#" class="facebook">Facebook</a>
CSS
.twitter {
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
background:red;
text-decoration: none;
}
.facebook {
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
background:blue;
text-decoration: none;
}
突然发现,两个按钮长得差不多,只是背景色不同而以,后来知道相同的样式可以合并在一起,于是我知道这样做也可以,还比前面方便一点:
.twitter,.facebook{
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
text-decoration: none;
}
.twitter {
background:red;
}
.facebook {
background:blue;
}
唯一变化,就是将.twitter
和.facebook
两个选择器合并到一起,使用公用样式部分。但也麻烦,如果有一天,我新增了.google
、.rss
等按钮呢?使用公用样式部分的选择器会变得非常的长,而且要不断的手工去添加。
随着看得多了,稍有点经验之后,我懂得给他们加一个公用的类名:
<a href="#" class=“btn btn-twitter”>Twitter</a>
<a href="#" class=“btn btn-facebook”>Facebook</a>
将公用的样式都写在.btn
类名上面:
.btn{
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
text-decoration: none;
}
.btn-twitter {
background:red;
}
.btn-facebook {
background:blue;
}
纳尼?这样也行。话说比前面方便些了,可又扯出另一个蛋疼的东西了,都得去动HTML。那么问题来了,有什么办法既不用动结构,又不用写那么多样式代码 ?其实这就是接下来要说的。这种情形Sass会变得更完美一些。
回到最初状态,我还是将结构写成:
<a href="#" class=“btn-twitter”>Twitter</a>
<a href="#" class=“btn-facebook”>Facebook</a>
在Sass中我先考虑到使用一个@mixin
,将公用样式都赋予给这个@mixin
:
@mixin btn{
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
text-decoration: none;
}
然后在实际中调用:
.btn-twitter {
background:red;
@include btn;
}
.btn-facebook {
background:blue;
@include btn;
}
编译出来的CSS:
.btn-twitter {
background: red;
border: 3px solid #000;
padding: 10px 20px;
color: #fff;
border-radius: 10px;
text-decoration: none;
}
.btn-facebook {
background: blue;
border: 3px solid #000;
padding: 10px 20px;
color: #fff;
border-radius: 10px;
text-decoration: none;
}
编译出来的代码跟初学CSS写出来的代码无两样。这对于一个有经验的CSSer来说,似乎无法接受。那就接着改良,咱们不使用@mixin
了,而换成基类.btn
,然后通过@extend
来继承:
.btn{
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
text-decoration: none;
}
.btn-twitter {
background:red;
@extend .btn;
}
.btn-facebook {
background:blue;
@extend .btn;
}
编译出来的代码:
.btn, .btn-twitter, .btn-facebook {
border: 3px solid #000;
padding: 10px 20px;
color: #fff;
border-radius: 10px;
text-decoration: none;
}
.btn-twitter {
background: red;
}
.btn-facebook {
background: blue;
}
这个回到当初选择器合并状态了,纠结的是,我本来只需要用的选择器合并在一起,现在还多了一个基类的,真是画蛇添足。得继续改,这回咱使用占位符%
:
%btn{
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
text-decoration: none;
}
.btn-twitter {
background:red;
@extend %btn;
}
.btn-facebook {
background:blue;
@extend %btn;
}
编译出来的CSS:
.btn-twitter, .btn-facebook {
border: 3px solid #000;
padding: 10px 20px;
color: #fff;
border-radius: 10px;
text-decoration: none;
}
.btn-twitter {
background: red;
}
.btn-facebook {
background: blue;
}
这样舒服多了,就算你还要增加新的按钮,这种方式也就只需要调用了。而不需要再去考虑HTML。
其实除了上面的方法之外,还可以使用Sass的map功能:
//SCSS
$vars:(
prefix-class: btn,
twitter: red,
facebook: blue
);
%btn{
border:3px solid #000;
padding:10px 20px;
color:#fff;
border-radius:10px;
text-decoration: none;
}
.#{map-get($vars,prefix-class)}-twitter {
background:map-get($vars,twitter);
@extend %btn;
}
.#{map-get($vars,prefix-class)}-facebook {
background:map-get($vars,facebook);
@extend %btn;
}
//CSS
.btn-twitter, .btn-facebook {
border: 3px solid #000;
padding: 10px 20px;
color: #fff;
border-radius: 10px;
text-decoration: none;
}
.btn-twitter {
background: red;
}
.btn-facebook {
background: blue;
}
其实实现上面的效果,在Sass中方法还有很多,大家应该通过实战或者经验,找出最适合自己的一种方案。
扩展阅读
- 使用Sass来写OOCSS
- 如何使用Sass和SMACSS维护CSS
- 写CSS最好方法:OOCSS+Sass
- OOCSS+Sass
- BEM修饰符:多类名 VS Sass @extend
- BEM在Sass3.4中的提升
- 引进AM-CSS属性模块
- Sass编写组件
编写Sass的技巧
编写Sass和CSS是类似的,初学者可能编写出来的Sass代码并不完美,甚至还会造成编译出来的CSS代码比自己使用CSS写出来的代码更垃圾。也很多同学因为这个原因而放弃了使用Sass这个高效的预处理器语言。其实不用担心,当你看得多、写得多、总结得多的时候,你的Sass代码会越来越优秀。总结几点我写Sass代码的体验:
- 组织好Sass文件:这一步非常重要,因为其直接影响你如何
@import
文件,更直接会影响你编译出来的代码会不会重复; - 有效使用Sass变量:Sass变量虽强大,但并不是一味的将所有东西都定义为变量,一难维护,二也不一定全部使用上;
- 减少对混合宏
@mixin
的依赖:@include
进来@mixin
,将会让你的代码变得更为冗余,保持一点,如果共用样式不涉及变量参数,坚决不使用混合宏来定义; - 拥抱
%placholder
:占位符%placeholder
在Sass中是一个好东东,只要不需要变量的公用样式(基类样式)都可以将他们定义成%placholder
; - 合理嵌套:保持你的嵌套不要超过三个层级,如果为了更好的编写或管理模块而使用选择器嵌套,也应该配合
@at-root
等相关功能,做到进退有度; - 使用
@function
做更多的事情:@function
可以帮助你定义更多的功能,帮你做更多的事情
其实使用Sass,大家应该时刻记得:*保持Sass的简单***。
扩展阅读
学习Sass的案例与资料
学习Sass其实最好的去处是Github。当然除了这个地方,你还可以在本站阅读Sass相关教程以及Sass中国网。下面列了几个国外关注度较高的Sass开源项目:
其实@结一同学也整理了几个好东东:
如果你对Sass的@mixin
和@functions
感兴趣,可以观注我创建的一个库,里面整理了一些自己常用的@mixin
和@functions
,而且还是一个非常有用的资源收藏夹,收录很多有关于Sass相关的教程与视频。
总结
花了一周多的时间整理了这篇文章,也算是自己学习Sass之后的总结吧,也可以说是系统的对W3cplus站上发布的Sass教程的整理。希望这篇文章对大家学习Sass、了解Sass和使用Sass有所帮助。更希望的是,如果您有相关方面的经验,希望与我们一起分享。
如需转载,烦请注明出处:http://www.w3cplus.com/preprocessor/sass-bring-change.html