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 for Web Designers》之为什么要使用Sass
- Sass有什么优势
- Less介绍及其与Sass的差异
Sass功能与特性
Sass现在更新到3.4.7版本(写这篇文章时最新版本),其实Sass有很多功能和特性是CSS无法匹敌的,比如:变量、继承、混合宏、占位符、逻辑判断、函数、@at-root
、列表和map
等等。这些都是Sass的基本功能,可以说掌握了这些功能,配合你自己的创造力,可以使用Sass做更多有意义的事情。
扩展阅读
- SASS基础教程——SASS基本语法与特性
- Sass的新特性
- Sass两个先进特性与局限性
- 关于Sass3.4你应该知道的一些事情
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,就不做过多阐述,因为现在的应用软件安装都是非常简单的,一路下一步即可。
扩展阅读
- Ruby安装
- Ruby安装
- CSS预处理器——Sass、LESS和Stylus实践【未删减版】
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进行开发,那么是不是直接通过引用
.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界面编译工具,目前较为流行的主要有:
- Koala
- Compass.app
- Scout
- CodeKit
- Prepros
自动化配置编译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编译
- Less/Sass编译工具,koala使用指南
- SASS界面编译工具——Codekit的使用
- SASS界面编译工具——Koala的使用
- Nodejs+Grunt配置SASS项目自动编译
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调试
- 实战Sass3.3的Source Maps
- Source Maps 101
如果掌握了上面提到的知识,我想你已具备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
的变量。
扩展阅读
- sass揭秘之变量
- 停止使用很多的Sass变量
- 【Sass初级】定义好的变量名
- 选择器使用Sass变量?
混合宏@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
调用的占位符,编译出来的代码会将相同的代码合并在一起。这也是我们希望看到的效果,也让你的代码变得更为干净。
扩展阅读
- 理解Sass的选择占位符
%placeholder
- Sass揭秘之
@mixin
,%
,@function
混合宏 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代码将会把相同的代码合并在一起。
扩展阅读
- Sass:Mixin还是Placeholder
- 理解SASS的嵌套,
@extend
,%Placeholders
和Mixins - 用Sass的占位符和混合宏创建可复用的样式
单类 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中级】模块化CSS更要避免选择器嵌套
- 【Sass初级】嵌套选择器规则
- 理解SASS的嵌套,
@extend
,%Placeholders
和Mixins
运算符
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; }
&
的奥妙尽在代码之中。其实你变换他的位置,简单点说吧,配合选择器嵌套,将会产生更多种不同组合效果,上面代码演示的是最常见的效果。如果您感兴趣,可以自己探索探索。
扩展阅读
- Sass3.3新特性之连体符
&
- Sass中连体符(
&
)的运用
列表$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"; }
扩展阅读
- 理解Sass的list
- 使用list-maps将你的Sass技术水平提高到另一层次
函数@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
中的
取值不同。配合through
的
值是12
,其遍历出来的终点值也是12
,和
值一样。配合to
的
值是13
,其遍历出来的终点值是12
,就是
对就的值减去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
可以实现一些复杂的功能。有兴趣的同学可以去尝试一下。
扩展阅读
- sass揭秘之
@if
,@for
,@each
- 了解Sass的控制命令
- Sass控制命令:
@if
,@for
,@each
和@while
@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等。
扩展阅读
- 组织Sass文件
- 管理Sass项目文件结构
- 【Sass初级】如何组织一个Sass项目
- 将你的CSS项目转换成Sass
OOSass
OOSass其实就是将OOCSS和Sass两者结合在一起的产物。虽然OOCSS给我们写样式带来重大的变革,但依旧是痛楚不断。其实将OOCSS和Sass结合在一起,你将不再那么痛苦。在此,用一个简单的示例来演示整个过程。
下面有两很拙的按钮:
不能再拙的按钮了,咱也不纠结了。记得当初刚接触这个行业时,我傻傻这样做:
HTML
<a href="#" class="twitter">Twittera> <a href="#" class="facebook">Facebooka>
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”>Twittera> <a href="#" class=“btn btn-facebook”>Facebooka>
将公用的样式都写在.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”>Twittera> <a href="#" class=“btn-facebook”>Facebooka>
在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
- 保持Sass的简单
- 提高Sass水平的小技巧
- Sass有什么优势
- 为什么要使用Sass
学习Sass的案例与资料
学习Sass其实最好的去处是Github。当然除了这个地方,你还可以在本站阅读Sass相关教程以及Sass中国网。下面列了几个国外关注度较高的Sass开源项目:
- Bootstrap
- Foundation
- Compass
- Bourbon
- Susy
- Gumby
- GroundworkCSS
- YAML
其实@结一同学也整理了几个好东东:
- Sass Guide
- Sass Core
- Tobe
如果你对Sass的@mixin
和@functions
感兴趣,可以观注我创建的一个库,里面整理了一些自己常用的@mixin
和@functions
,而且还是一个非常有用的资源收藏夹,收录很多有关于Sass相关的教程与视频。
Sass Mixins & Functions
总结
花了一周多的时间整理了这篇文章,也算是自己学习Sass之后的总结吧,也可以说是系统的对W3cplus站上发布的Sass教程的整理。希望这篇文章对大家学习Sass、了解Sass和使用Sass有所帮助。更希望的是,如果您有相关方面的经验,希望与我们一起分享。