作者:无忧
关注可了解更多的前端教程。问题或建议,请留言;
如果你觉得无忧对你有帮助,欢迎点赞[1024]
一. Sass概况
今天来聊聊sass吧,之前用了很久的less,刚开始接触的时候感觉这东西就是个神器,但是对比之下,还是sass更香。写css时间长了自然就能发现css在书写的时候的不足之处,不能嵌套,没有变量,更加不能像js那样用循环自动生成之类的,这些问题全部都被less解决掉了
less加入了变量,加入了嵌套式写法,还有一大堆好用的功能,在这里就不多说了,今天要说的是sass。其实刚接触less的时候就知道有sass这个东西了,但是那时候的sass很奇怪,写法过于新颖,大家感受一下
body
margin: 0
padding: 0
height: 20px
width: 30px
...
看出来了么? 对的,sass早期的时候有规定书写格式,不这么写它就给你报错,这个在习惯上来说一时间是比较难接受的,特别是我们这种写css好几年的人来说更加如此,而后面sass的升级版又把它强加的书写格式去掉了可能就是考虑到这方面
第二个不能接受sass的点就是安装比较麻烦,从事前端开发的人员,电脑上基本上是少不了node的 less可以直接用npm包管理器进行安装,这就很方便了,而sass就相对麻烦一些了,还需要先安装ruby。 因为sass是基于ruby语言开发的(mac系统自带ruby,后来才知道)从过程来说就多了一步。另外一点,less可以作为一个js文件直接在客户端运行,这无疑又拉近了和前端开发者之间的距离,所以早期的时候大家都比较倾向于less。
Bootstrap也是这么选择的,Bootstrap 3.0就是用less进行开发的。而对于前几年来说,less也足以满足大家的需求了。
但是经过这几年的发展,大家的技术在进步,需求也在变得更加复杂,这时候less就显得有点力不从心了,并不是它不好了,只是对于工作几年的中高级程序员来说,大家需要一个功能更加强大的css预处理器。
于是这时候回头一看,sass一直都在我们身边,看看它的文档,虽然写得有点乱,但是各方面都比较符合我们的需求,越看越顺眼了。而且现在的sass也升级了,做到了完全兼容css3, 在css基础上增加变量,嵌套,混合等功能。可以通过函数进行颜色值与属性值的运算,提供控制指令等高级功能,甚至可以自定义输出格式
另外一点,随着各大框架的推行,less和sass的编译工作变得越来越简单,安装个loader就好了,不需要全局安装编译器了。另外很多编辑器也自带编译,这样一来,less前期的那些优势荡然无存。 那,接下来,跟我一起来看看sass吧
通过前面的介绍,我想大家已经知道了,sass是一个css预处理语言。 在这里要明确一个概念,什么是css预处理语言呢?
简单来说CSS 预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为 CSS 增加了一些编程的特性,将 CSS 作为目标生成文件,然后开发者就只要使用这种语言进行编码工作。在编程语言中的一些基本特性,可以让你的 CSS 更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。
再通俗一点,就像是文言文和白话文。 文言文几个字,翻译成白话文就是一片文章。当然在这里只是一个类比。
基本概念理解清楚了,Sass有这么多好用的功能,但是我要如何把sass用到我的项目当中呢?请往下看
二. 安装和使用
前面已经提到了,Sass是用Ruby语言开发的,所以在安装Sass之前,需要先安装Ruby
这里要注意几点:
- Mac系统是自带Ruby的,所以就不需要再次安装啦,所以下面安装Ruby的方法也只适用于Windows系统。
- 一些基于插件的编辑器,比如vsCode,Atom之类的,不需要安装sass,直接安装插件就可以进行编译。
Windows下安装Sass首先需要安装Ruby, 先从官网下载Ruby并安装。安装的过程中请注意勾选 Add Ruby executables to your PATH添加到系统环境变量。如下图:
安装完成之后需要测试安装有没有成功,在命令行工具(cmd)里面运行以下下命令:
rubu -v 如果安装成功会打印下面这句话: ruby 2.2.2
Ruby安装好了之后,我们就可以利用Ruby的包管理工具gem安装Sass了,同样还是在Cmd里运行以下命令:
gem install sass
安装完成之后,还是老办法检查以下安装是否成功:
sass -v
如果安装成功,则会打印以下代码:
Ruby Sass 3.7.4
看到这里,我们基本上就可以顺利的玩耍啦~ 附上一些其他相关命令:
更新sass:
gem update sass
查看sass版本:
sass -v
查看sass帮助:
sass -h
现在我们写去一点sass代码吧:
body{
margin: 0;
background: #fefefe;
}
.box{
width: 100px;
height: 100px;
border: 1px solid #899;
}
你没有看错,以上看起来和css简直一模一样的代码其实就是sass代码!是不是很神奇?当然这段代码编译使用过后的效果当然也就和css一样啦。看到这里,我想说的是,大家学习sass千万不要有任何的负担,因为安装好sass之后,sass就可以和css一样写,如果你现在还没有任何的css预处理语言的编写经验,你完全可以像以前写css那样来写代码。如果你在看这篇文章的过程当中,一不小心学会了一招半式的,那就是提升了。
** 注意啦 **
不过这里有一点需要提一下,sass有两个文件后缀,一个是.sass 一个是.scss scss是sass的升级版,他们都统称Sass 只是文件后缀不一样。然后.sass后缀的文件是固定代码格式的,就是在一开始我提到的那样写:去掉“{}”,去掉“;” 这让代码看起来有点怪,不过习惯就好了。 而.scss后缀结尾的文件就比较友好了,怎么写都可以,而且它是升级版,所以我们以后在使用的过程中就用.scss就好了
写好了sass代码之后,我们就需要把sass编译成css 然后应用到我们的项目当中了。这个过程就可以称为编译了。
三. 编译(就是把sass翻译成css)
sass编译有很多种方式,如命令行编译模式、编辑器自动编译、编译软件koala、sass-loader等。
先来看第一种:命令行编译
刚才我在test文件夹里面已经建立了一个style.scss文件,现在我需要把这个文件编译成css文件,那么我可以先用命令行工具进入这个文件夹 “cd”就是进入某个文件夹的命令,后面跟上你电脑上文件夹的路径就可以了:
cd test
进入文件夹之后再使用sass的编译命令:
sass input.scss output.css
编译成功不会有任何的提示,但是文件夹里面马上就会出现output.css这个文件
这个input是原本的scss文件的名称,然后output是编译出来之后的css文件的名称,这个名称大家可以随意,保持基本命名规则就可以了
利用命令还可以实时监测某个文件,当这个文件发生变化的时候,自动完成编译过程
//单文件监听命令
sass --watch input.scss:output.css
在监听成功之后会打印以下代码:
>>> Sass is watching for changes. Press Ctrl-C to stop.
根据提示,这时候按Ctrl + C可以停止这个命令
在监听的过程中如果文件发生改变,并且自动编译成功则会打印以下代码:
>>> Change detected to: tests.scss
write output.css
write output.css.map
利用命令还可以实时监测某个文件夹,这个文件夹下的所有scss文件都会被监听
sass --watch targetFolder:outputFolder
在监听成功之后会打印以下代码:
>>> Sass is watching for changes. Press Ctrl-C to stop.
前面的targetFolder是要监听的目标文件夹,后面的outputFolder是要输出的文件夹。 比如我想把css文件夹里的scss文件编译到style文件夹里,就可以这样写:
sass --watch css:style
在这里要注意一点: 这个时候你需要在css和style文件夹的共同父级文件夹里面才能开启这个功能,否则会报错
在编译的时候,还可以配合编译选项,用于调整输出文件
sass提供四种编译格式:
- nested
- expanded
- compact
- compressed
看看区别:
//未编译样式
.box {
width: 300px;
height: 400px;
&-title {
height: 30px;
line-height: 30px;
}
}
..................................................
nested 编译排版格式:
/*命令行内容*/
sass style.scss:style.css --style nested
/*编译过后样式*/
.box {
width: 300px;
height: 400px; }
.box-title {
height: 30px;
line-height: 30px; }
..................................................
expanded 编译排版格式:
/*命令行内容*/
sass style.scss:style.css --style expanded
/*编译过后样式*/
.box {
width: 300px;
height: 400px;
}
.box-title {
height: 30px;
line-height: 30px;
}
..................................................
compact 编译排版格式
/*命令行内容*/
sass style.scss:style.css --style compact
/*编译过后样式*/
.box { width: 300px; height: 400px; }
.box-title { height: 30px; line-height: 30px; }
..................................................
compressed 编译排版格式
/*命令行内容*/
sass style.scss:style.css --style compressed
/*编译过后样式*/
.box{width:300px;height:400px}.box-title{height:30px;line-height:30px}
不同的编译格式得到的代码其实是一样的,只是排版格式不同,大家看情况去用就好了。我的建议是,开发的时候用expanded格式,比较清楚。当你需要发布你的代码的时候,使用compressed格式,这个格式会让css文件相对较小。
第二种:编辑器直接编译(以webStorm为例)
相对于第一种编译方式来说,用编辑器来帮助我们编译则省去了我们在开发过程中的很多操作,比如用cd命令进入某个文件夹,退出文件夹之类的,并且不是所有人都熟悉命令操作方式的。这时候就显得这种编译方式可能会更加好用,我在工作过程中大多数也是用这种方式来进行编译的,来看具体操作吧:注意,用编辑器编译的基础也是安装Sass 所以Sass的安装过程是必不可少的!!!
既然想要让编辑器帮助我们做事情,那么就需要对编辑器的一些配置进行调整。
依次打开 setting > tools > file Watchers,界面大致如下图:
打开之后点击左下角的加号,在弹出的界面里选择scss
选择之后,会弹出一个窗口:
在这个窗口里有两项需要调整,我已经用红框标出来了
第一项,Program 这里是需要选择编译所用的程序,点击后面的文件夹图标进行选择
- windows电脑在安装Ruby的文件夹里,如果没有修改的话一般是在C盘: C:/user/username/AppData/Roaming
- Mac电脑一般在 “usr/local/bin” ,在选择文件的窗口里按快捷键: Command + Shift + G 然后输入:usr/local/bin回车,就可以找到对应的文件夹了
在文件夹里找到一个名字为scss的文件:
第二项,Arguments 这里主要是设置编译参数,比如前面我们提到的四种编译格式就可以加载这里面 具体看前面的图片
至于其它编辑器,比如VsCode就更加简单了,它不需要安装sass,直接安装插件就可以了,在插件库里搜索sass,随便挑选一个安装就可以了,我用过的插件是 Live Sass Compiler。
第三种:编译软件 Koala
附上官网地址: http://koala-app.com/这是一个小软件,在官网上下载安装就可以了,官网可以自动分辨是windows系统还是Mac系统,所以直接点击下载就可以了。
根据提示,直接把要编译的文件拖到软件里或者把整个文件夹拖到软件里就可以了
点击某个文件,右边就会弹出来一个菜单,勾选最上面的 Auto Complie就可以自动监听啦,然后下面有一些选项,比如我们之前提到过的 四种编译格式:sass提供四种编译格式: nested、expanded、compact、compressed都可以在这里进行选择,最后点击最下面的compile就完成了
第四种,在框架里用sass
今天的前端环境,框架已经成为开发主流了,所以这方面需要大家下点功夫去研究了,在这我就说在Vue里该如何去是用sass吧首先我们用Vue Cli来搭建一个简单的项目
先安装Vue Cli,对Vue Cli不了解的话可以去官网看看介绍
Vue Cli可以通过npm包管理器直接安装,所以我们可以直接输入命令:
npm install -g @vue/cli
安装好Vue Cli之后就可以直接使用Vue命令创建一个项目啦:
vue create projectname
在这里注意几点:
注意先进入你要创建项目的文件夹,用cd命令
- projectname你可以自定义,符合命名规范即可,不过不能出现大写字母
- vue创建项目之后,会在你的文件夹里面新建一个以projectname命名的文件夹,项目的主目录也即是它了
- 创建项目之后需要进入到项目文件夹里才能运行项目
创建好项目之后用编辑器打开,大概就长这样了:
Vue Cli已经帮我们把必要的东西都集成进去了,不过我们还需要安装两个依赖,一个是sass-loader sass-loader依赖于node-sass, 所以还需要安装一个node-sass, 直接在编辑器里打开命令行工具进行安装就可以了,这样可以省去用cd命令进去我们项目文件夹的时间
打开之后:
注意观察当前所处的文件夹!!!然后直接输入命令:
npm install sass-loader node-sass --save-dev
原本sass文件处理好了之后还需要交给css-loader style-loader都处理一遍的,但是Vue Cli已经帮我们处理好了,就连webpack的配置都已经处理好了
所以我们就不需要去管后面的过程了,当然有兴趣的同学也可以去了解一下webpack里面会有详细的解说.
安装好了之后在package.json文件里可以看到,版本可能不一样,这种细节不需要在意,嘿嘿~
一切准备就绪,接下来就可以开心的撸代码啦!
随便找一个Vue文件,然后在style里面就可以直接写sass代码了,不过要注意在style标签上加上 lang="scss" 这个属性,注意在这里scss和sass是有区别的哦!
然后直接输入命令:
npm run serve
把项目跑起来就可以啦!
常见node-sass安装失败处理方法: 把npm资源切换到国内淘宝镜像:
- 淘宝 npm 地址: npm.taobao.org/
- 通过cnpm使用 :npm install -g cnpm --registry=registry.npm.taobao.org
- 临时使用:npm --registry registry.npm.taobao.org install express
四. 语法
通过前面几点,Sass的使用说明书已经发放给各位了,还没来得及看的同学可以往上翻翻,这里的主旨是Sass的功能说明书,各位看官准备好瓜子水果...1,变量
变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。由于变量让你能够把程序中准备使用的每一段数据都赋给一个简短、易于记忆的名字,因此它们十分有用。
其实简单来说,就是给人起外号,我觉得250px这个值不好看,我给它起个外号叫SB,于是我以后就可以这么写了:
$SB:250px;
.box{
width: $SB;
height: 20px;
}
现在,box的宽度虽然我写的是SB,但是它实际代表的是250px。至于前面那个美元”$”符号,它其实是一个标识,告诉Sass, 这是一个外号,并不是一个真SB。
有了这个外号之后就很方便啦,以后如果想要修改box的宽度,直接喊一声:SB变身!变成100px,然后box的宽度就变成100px了。
来看个案例:
现在我在页面上写了一个矩形,一个三角形,一个按钮。这些都是平常我们页面中很常见的元素,它们都用了一个我也说不清的颜色,总之不是太好看,暂且先不管,来看看代码:
$color: #899;
.box{
width: 200px;
height: 200px;
border: 5px solid $color; /*-------------*/
float: left;
}
.arrow{
margin: 100px;
width: 0;
height: 0;
border-width: 40px 20px 0;
border-style: solid;
border-color: $color transparent transparent transparent; /*-------------*/
float: left;
}
.btn{
width: 100px;
height: 40px;
margin-top: 96px;
border: 1px solid $color; /*-------------*/
border-radius: 4px;
color: $color;
text-align: center;
line-height: 40px;
float: left;
}
在代码的开头我定义了一个变量:$color: #899; 然后分别用在了代码中的标记处。那这时候我们就可以说,上面图中的三个元素,它们的颜色都是由变量 $color来控制的,现在这个颜色不好看,说不定哪天UI小姐姐就会让我改,但是没关系,我们有变量,当需要修改颜色的时候,直接去修改变量的值就可以了 比如现在我们要把他们变成粉色的:
这时候我只需要把变量修改掉就好了
$color: #f991ff;
得到结果:
另外,既然说到变量,那么变量它是有作用范围的,它只能在一定范围里起作用,比如我在a.scss中定义了 变量$color 那么在b.scss中就不能使用了,只能在a.scss中使用。
现在我们知道了在a.scss中声明的变量只能在a.scss中使用了,其实这也是有前提的,这个前提就是这个变量的声明没有被任何东西所包裹。比如说:
.box{
$zhuangBiFan: #00afff;
width: 200px;
height: 200px;
border: 5px solid $color;
float: left;
}
在这段代码里,变量 $zhuangBiFan 就可以说被选择器 .box 包裹起来了,那么这时候这个变量在 .box的外面就不能使用了 这时候我们可以说变量 $zhuangBiFan 的作用范围就是在选择器.box里面。 作用范围,还有一个比较专业的名称,叫做作用域,其实域就是范围的意思,只不过叫作用域会显得稍微高大上一点点,所以就叫他作用域吧~
不过呢,Sass还提供了一种变量越狱的方法,那就是在变量的后面加上一个global :
#main {
$width: 5em !global;
width: $width;
}
#sidebar {
width: $width;
}
在这个情况下,#main里面的变量在#sidebar里面也可以使用(咋有种红杏出墙的赶脚)
另外有趣的一点是:变量的值可以引用其他变量!!!
$color: #f991ff;
$globalBorder: 1px solid $color;
.box{
border: $globalBorder;
}
编译之后:
.box {
border: 1px solid #f991ff;
}
原生css中的变量 既然提到变量,那就不得不提一嘴,css3其实也已经有变量了,只不过定义和使用没有sass的这么方便而已 在我的另一篇文章:中我就用到了原生的css变量。 里面有这么一段:
.eyes:before,
.eyes:after{
content: '';
position: absolute;
top: 110px;
width: 100px;
height: 100px;
background-color: #4e4e4e;
border-radius: 50%;
background-image:
radial-gradient(circle at var(--left1), white 1px, transparent 1px),
radial-gradient(circle at var(--left2), black 10px, transparent 11px),
radial-gradient(circle at var(--left3), white 30px, transparent 31px);
}
.eyes:before{
left: 26%;
--left1: 84px;
--left2: 80px;
--left3: 64px;
}
.eyes:after{
right: 15%;
--left1: 16px;
--left2: 19px;
--left3: 36px;
}
这里面就用到了变量,变量分别是left1、left2和left3。这些变量由于作用域的原因分别给before和after附上了不同的值 所以最后得到的结果也是不一样的 简单说一下css的变量声明方式:
声明变量:
--name:value;
使用的时候:
var(--name)
不过这里要注意,css变量的声明必须被包含,不能直接 ——name:value; 可以考虑这么写:
:root{
--name:20px; /*声明*/
}
.box{
width: var(--name); /*使用*/
}
2,嵌套
Sass 允许将一套 CSS 样式嵌套进另一套样式中,内层的样式将它外层的选择器作为父选择器, 考虑用css实现下面这个布局: 那么用css 我们可能会这样写:
nav {
height: 40px;
border-bottom: 1px solid #333;
line-height: 40px;
}
nav ul {
float: left;
height: 100%;
}
nav ul li {
float: left;
height: 100%;
border-right: 1px solid #333;
padding: 0 20px;
}
可以看到后面两个选择器都有共同的父级 nav, 现在我们用sass的嵌套来重写一下这段代码:
nav{
height: 40px;
border-bottom: 1px solid #666;
line-height: 40px;
ul{
float: left;
height: 100%;
li{
float: left;
height: 100%;
border-right: 1px solid #333;
padding: 0 20px;
}
}
}
编译之后的结果:
nav {
height: 40px;
border-bottom: 1px solid #666;
line-height: 40px;
}
nav ul {
float: left;
height: 100%;
}
nav ul li {
float: left;
height: 100%;
border-right: 1px solid #333;
padding: 0 20px;
}
可以看到编译过后的代码和之前的css代码是一模一样的,但是sass代码明显层级结构就明显很多,不需要去看无关的代码,一眼就可以看出来nav里包含了ul 然后ul里又包含了li。所有被包含的选择器都会被当做外层选择器的子级。在编译的时候,sass会把父级选择器的名字拿出来,然后加上子级选择器本身,中间加上一个空格,然后输出到css 于是就组成了我们看到的编译过后的正常的css代码。不过这也会造成一个问题。
同样还是上面的布局,这时候我们考虑给li加上一个鼠标悬停的伪类,当鼠标放到li身上的时候,我们希望li的背景颜色变成灰色,文字颜色变成白色,所以我们可能会写如下代码:
nav{
height: 40px;
border-bottom: 1px solid #666;
line-height: 40px;
ul{
float: left;
height: 100%;
li{
float: left;
height: 100%;
border-right: 1px solid #333;
padding: 0 20px;
:hover{
background: #999;
color: #fff;
}
}
}
}
乍一看好像是没毛病,但是我们来看看编译过后的代码:
nav {
height: 40px;
border-bottom: 1px solid #666;
line-height: 40px;
}
nav ul {
float: left;
height: 100%;
}
nav ul li {
float: left;
height: 100%;
border-right: 1px solid #333;
padding: 0 20px;
}
nav ul li :hover {
background: #999;
color: #fff;
}
仔细看看li的伪类样式我们会发现 原本期望的li:hover中间多了一个空格, 当然这也符合sass的编译规则,把所有父级选择器的名称拿出来,和子级选择器组合成一个后代选择器,而后代选择器中间是需要一个空格的。但是现在的情况是我们需要把这个空格去掉,这时候就需要我们手动告诉sass要怎么做了,而承担这个功能的就是 “&”这个符号了:
nav{
height: 40px;
border-bottom: 1px solid #666;
line-height: 40px;
ul{
float: left;
height: 100%;
li{
float: left;
height: 100%;
border-right: 1px solid #333;
padding: 0 20px;
&:hover{
background: #999;
color: #fff;
}
}
}
}
可以看到我在:hover前面加上了 “&”符号,这么一来编译就正常了:
nav {
height: 40px;
border-bottom: 1px solid #666;
line-height: 40px;
}
nav ul {
float: left;
height: 100%;
}
nav ul li {
float: left;
height: 100%;
border-right: 1px solid #333;
padding: 0 20px;
}
nav ul li:hover {
background: #999;
color: #fff;
}
其实在这里,sass的解释是:&符号会替换最近层级的父选择器和子级组成后代选择器,那么在这里的过程就是:
- 先拿到所有父级选择器的名称:nav ul li 然后和 &:hover组成后代选择器
- 然后把&符号替换掉最近层级的父选择器 组成 nav ul li:hover组成后代选择器
所以经常可以见到有些人会这么写:
.form {
padding-top: 0.44rem;
&__item {
position: relative;
height: 1.173333rem;
&-clear {
position: absolute;
bottom: 0.253333rem;
}
&-imgcode {
position: absolute;
top: 0.253333rem;
img {
width: 100%;
height: 100%;
}
}
}
&__submit {
display: block;
text-align: center;
font-family: 'PingFangSC-Medium';
&--disabled {
background-color: #899;
}
}
input {
height: 0.493333rem;
font-size: 0.346667rem;
}
}
编译过后:
.form {
padding-top: 0.44rem;
}
.form__item {
position: relative;
height: 1.173333rem;
}
.form__item-clear {
position: absolute;
bottom: 0.253333rem;
}
.form__item-imgcode {
position: absolute;
top: 0.253333rem;
}
.form__item-imgcode img {
width: 100%;
height: 100%;
}
.form__submit {
display: block;
text-align: center;
font-family: 'PingFangSC-Medium';
}
.form__submit——disabled {
background-color: #899;
}
.form input {
height: 0.493333rem;
font-size: 0.346667rem;
}
可以看到这样写sass代码的一个好处是:
- 编译过后所有的选择器层级都只有一层
- 所有的选择器名称都带上了父级选择器的名称,有效避免命名冲突
- sass代码还是可以很清晰的看到代码的层级
关于嵌套,sass还有一个比较好玩的东西,叫做属性嵌套 有些 CSS 属性遵循相同的命名空间 (namespace),比如 font-family, font-size, font-weight 都以 font 作为属性的命名空间。为了便于管理这样的属性,同时也为了避免了重复输入,Sass 允许将属性嵌套在命名空间中,例如:
.funky {
font: {
family: fantasy;
size: 30em;
weight: bold;
}
}
编译过后:
.funky {
font-family: fantasy;
font-size: 30em;
font-weight: bold; }
甚至命名空间也可以包含自己的属性值,例如:
.funky {
font: 20px/24px fantasy{
weight: bold;
}
}
编译过后:
.funky {
font: 20px/24px fantasy;
font-weight: bold; }
css中类似的属性有很多,比如border、background、margin、padding等.
3,混合
前面我们已经知道sass中的变量了,但是变量能记录的毕竟只是一个值,顶多把一个属性的所有值全部记录进去,比如这样:
$primary-border:1px solid #899;
.box{
border:$primary-border;
}
这样固然可以让box这个选择器很轻松地获得一个边框样式,但是如果我有大量公用的样式呢?比如文字颜色,字体大小这些东西其实都可以统一起来,那这时候如果我们再一条一条的去存变量,然后再去使用的话就不太方便了。混合(Mixin)就是用来解决这个问题的,它可以把一整段代码打包,然后像一个变量一样在其他地方使用。
混合在官方的解释是这样的:混合指令(Mixin)用于定义可重复使用的样式,避免了使用无语意的 class。混合指令可以包含所有的 CSS 规则,绝大部分 Sass 规则,甚至通过参数功能引入变量,输出多样化的样式。
来看案例:
考虑图中按钮的实现,咱先用以前的方法来写:
$btnHeight: 40px;
$grayBorder: 1px solid #bbb;
$blueBorder: 1px solid #4395ff;
.btnGray{
height: $btnHeight;
border: $grayBorder;
padding: 20px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
}
.btnBlue{
height: $btnHeight;
border: $blueBorder;
padding: 10px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
}
可以看到,以上代码中虽然我们大量用了变量,但是两个选择器里面内容的相似度依然非常高,大家可能在想,明明border-radius、font、text-align都可以用变量来定义,但是仔细想想,这样的代码真的是我们需要的么? 稍微复杂点就全都是变量? 这恐怕会越写越乱吧!所以面对这样的代码,我们就需要用到混合了
混合
现在我们来看看用混合怎么写:
@mixin btn{
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
}
.btnGray{
@include btn;
border:1px solid #bbb;
padding: 20px;
}
.btnBlue{
@include btn;
border:1px solid #4395ff;
padding: 10px;
}
编译之后:
.btnGray {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #bbb;
padding: 20px;
}
.btnBlue {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #4395ff;
padding: 10px;
}
可以看到这代码一下就变得很清晰了。我们来看看这段代码里发生了什么事情: 第一段,利用@mixin指令创建了一个“超级变量”:btn,而这个“超级变量”是一个集合,集合里面可以写所有的css规则。然后通过@include指令引用这个“超级变量”。这样我们就做到了把几个相似选择器中重复的代码提取出来,达到了代码的高复用性。而sass把这个定义“超级变量”, 和引用“超级变量”叫做混合。
当然在这之前我们可能会考虑写一个公用的class,然后在需要的地方去引入这个class就可以了,但是那样的代码毕竟可维护性不高,性能也是肯定不如sass的,而且sass的混合还有更加强大的地方,往下看。
混合中的参数
在上面这段代码中,虽然我已经把重复的代码全部都提取出去了,但是剩下的两个属性:border和padding两个选择器之间的差别也不太大,那有没有可能把这两个属性也提取出来呢?是可以的:
@mixin btn($color){
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid $color;
}
.btnGray{
@include btn(#899);
padding: 20px;
}
.btnBlue{
@include btn(#4395ff);
padding: 10px;
}
编译过后:
.btnGray {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #899;
padding: 20px;
}
.btnBlue {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #4395ff;
padding: 10px;
}
可以看到这段代码里,编译过后的代码和之前的代码是一样的,但是sass代码又更加精简一些了 ———— 在定义混合样式 btn 的时候,在后面加上了一个括号,括号里其实就是一个变量名称,但是这个变量并没有值。然后可以看到这个变量在下面用到了 : border: 1px solid $color; 在这里用到了这个变量。
那么也就代表了,在这个混合样式没有被调用的时候,这个border里面的$color是得不到值的,只有当调用这个混合样式的时候,给它传入一个值,这时候border才能得到传入进来的值,然后把传入进来的值替换到border这条样式上去,然后在把自己作为一个整体替换到引入混合样式的地方。这个过程看起来可能会有点复杂,这么说:
我们把这整个过程当做是去存物处取东西,不同的客户拿着不同的钥匙来,那么取走的东西肯定也是不一样的,但是存物处突然搞活动了,所有客户来取东西,都送一盒鸡蛋!那么这时候我们整个过程就比较好理解了,不同的客户(.btnGray、.btnBlue)拿着不同的钥匙(#899、#4395ff)来btn这个存物处这里取东西,那么btn会根据不同的钥匙取出不同的东西(.btnGray得到的是:border: 1px solid #899, 而.btnBlue得到的是:border: 1px solid #4395ff)。然后附赠一盒鸡蛋(公共样式) 最后,.btnGray和.btnBlue都得到了公共样式,并且分别得到了颜色不一样的边框样式~
@mixin btn($color){
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid $color;
}
.btnGray{
@include btn(#899);
padding: 20px;
}
.btnBlue{
@include btn(#4395ff);
padding: 10px;
}
再来看这段代码,从上到下解析,从1~7行一开始是不解析的,因为在这期间它作为一个混合样式还没有被调用。所以不会解析
到第10行的时候看到了一个 @include指令,并且后面跟着的名称是 btn .那这时候sass会去找有没有名字叫做btn的混合样式,哎,貌似刚才经过的地方有这么个家伙,感觉有点眼熟,然后立马把17行的btn抓过来了:这里要调用你了! 再往后一看,哎哟,居然还需要钥匙,恰好第10行代码 btn的后面的括号里就有一个钥匙,于是乎17行在这一瞬间就被替换成了这样:
@mixin btn{
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid #899;
}
然后再把这一段代码复制到第10行这个地方,于是选择器.btnGray就获得了完整的代码。替换完成了之后 1~7行就立刻又恢复原样了。代码继续往下走,走到第15行的时候,哎,这个地方还需要那哥们,于是btn又被拎过来了... 重复刚才的动作。
要注意的点:定义了参数的混合样式,必须传入参数,否则会报错
到这里,混合这个东西似乎就比较清楚了。不过细心的同学可能发现了,貌似两个选择器里的padding也可以这样操作呀?
@mixin btn($color,$pad){
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid $color;
padding: $pad;
}
.btnGray{
@include btn(#899,20px);
}
.btnBlue{
@include btn(#4395ff,10px);
}
是的,混合样式后面的的变量其实叫做参数,参数用于给混合指令中的样式设定变量,并且赋值使用。在定义混合指令的时候,按照变量的格式,通过逗号分隔,将参数写进圆括号里。引用指令时,按照参数的顺序,再将所赋的值对应写进括号
要注意的点:多个参数在调用传值时,只能按照定义的顺序传入,中间不可缺少参数 如果想要打乱顺序,则可以这样写:
.btnGray{
@include btn($pad: 20px, $color: #899);
}
参数默认值
甚至混合指令也可以使用给变量赋值的方法给参数设定默认值,然后,当这个指令被引用的时候,如果没有给参数赋值,则自动使用默认值:
@mixin btn($color: #899, $pad: 20px){
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid $color;
padding: $pad;
}
这样的话变量pad 就拥有默认值了,在调用的时候,如果没有传入值,那么这两个变量将会得到这里定义的默认值
.btnGray{
@include btn;
}
编译之后:
.btnGray {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #899;
padding: 20px;
}
要注意的点:如果定义了多个变量,当只想改变其中一个变量时,可以指定变量名,比如现在我们的混合样式已经带有默认值了,现在我需要让.btnGray的padding值变成10px 要怎么办呢?
如果我们这样写:
.btnGray{
@include btn(10px);
}
编译之后:
.btnGray {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid 10px; /*传入的第一个参数默认会放在这个位置*/
padding: 20px;
}
这样无疑是错误的,所以我们可以指定其变量名:
.btnGray{
@include btn($pad: 10px);
}
编译之后:
.btnGray {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #899; /*这时候这里会使用默认值*/
padding: 10px; /*指定变量替换这里的值*/
}
一些小技巧
1.在混合样式里,可以引入其他混合样式:
@mixin compound {
@include highlighted-background;
@include header-text;
}
@mixin highlighted-background { background-color: #fc0; }
@mixin header-text { font-size: 20px; }
.box{
@include compound;
}
编译之后:
.box{
background-color: #fc0;
font-size: 20px;
}
2.可以把整个选择器当做混合样式的一部分,这在代码模块化的时候非常有用:
@mixin silly-links {
a {
color: blue;
background-color: red;
}
}
.box{
@include silly-links;
}
编译成:
.box a {
color: blue;
background-color: red;
}
3.当不确定参数个数的时候,可以这样写:
@mixin box-shadow($shadows...) {
-moz-box-shadow: $shadows;
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
}
.shadows {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}
4,继承
@extend 继承
在设计网页的时候常常遇到这种情况:一个元素使用的样式与另一个元素完全相同,但又添加了额外的样式。通常会在 HTML 中给元素定义两个 class,一个通用样式,一个特殊样式。那么通过@extend我们可以这样写:
通用样式:
.normal{
color: #00d5e8;
background: #e8e000;
}
特殊样式:
.special{
@extend .normal; /* 继承normal选择器里的所有样式 */
padding: 10px;
}
编译过后:
.normal, .special {
color: #00d5e8;
background: #e8e000;
}
.special {
padding: 10px;
}
可以看到special继承了normal之后,他们两都会拥有color和background这两条属性,于是sass把这两条属性放在一起组成一个群组选择器,然后再把special所独有的样式拿出来形成一个选择器。
还是来看这个案例:
考虑一下实现方式:
.btn{
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
}
.btnGray{
border:1px solid #bbb;
padding: 20px;
}
.btnBlue{
border:1px solid #4395ff;
padding: 10px;
}
html:
<div class="btn btnGray"></div>
<div class="btn btnBlue"></div>
这是我们在css时代常有的做法。现在又了继承之后,我们可以考虑节省一些代码了:
.btnGray{
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid #bbb;
padding: 20px;
}
.btnBlue{
@extend .btnGray;
border:1px solid #4395ff;
padding: 10px;
}
编译过后:
.btnGray, .btnBlue {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #bbb;
padding: 20px;
}
.btnBlue {
border: 1px solid #4395ff;
padding: 10px;
}
大家可能会想我为什么不直接让btnGray和btnBlue去继承 btn 呢? 大家注意看最上面的代码。normal作为被继承的样式,经过编译之后也会出现在css文件当中。 而在这个案例中,我们不需要单独的btn类,所以我直接让 btnBlue 继承 btnGray 的所有样式,然后再去添加 btnBlue所独有的样式就可以了,利用css代码的层叠效果可以自动把重复的代码覆盖。效果上来说是完全不受影响的。
继承的过程中,甚至会把被继承样式的相关样式也继承过来,比如我们给 btnGray 加上一个hover伪类,还记得在嵌套中怎么写伪类么?
.btnGray{
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid #bbb;
padding: 20px;
&:hover{
background-color: #fefefe;
}
}
.btnBlue{
@extend .btnGray;
border:1px solid #4395ff;
padding: 10px;
}
编译过后:
.btnGray, .btnBlue {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #bbb;
padding: 20px;
}
.btnGray:hover, .btnBlue:hover {
background-color: #fefefe;
}
.btnBlue {
border: 1px solid #4395ff;
padding: 10px;
}
可以看到 btnGray 和 btnBlue 都具有了 hover 这个伪类,那再添加一些其他的样式呢?
.btnGray{
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border:1px solid #bbb;
padding: 20px;
&:hover{
background-color: #fefefe;
}
.item{
width: 200px;
height: 200px;
}
}
.btnBlue{
@extend .btnGray;
border:1px solid #4395ff;
padding: 10px;
}
编译过后:
.btnGray, .btnBlue {
height: 40px;
border-radius: 4px;
font: 24px/40px '微软雅黑';
text-align: center;
border: 1px solid #bbb;
padding: 20px;
}
.btnGray:hover, .btnBlue:hover {
background-color: #fefefe;
}
.btnGray .item, .btnBlue .item {
width: 200px;
height: 200px;
}
.btnBlue {
border: 1px solid #4395ff;
padding: 10px;
}
看到这里,大家应该也看出来了,这玩意和 混合@mixin很相似啊。那我们以后到底是选择继承还是选择混合呢?
在这里我们首先要总结一下混合与继承的区别:
- 继承会把被继承的样式也输出到css文件,而混合样式不会被编译到css文件中
- 继承会尽量把代码整合在一起,组成群组选择器,而混合并不会做这件事
- 混合可以定义参数而继承是不行的
大多数情况下,其实@mixin会比@extend更好,但是它们俩都有自己的一席之地。当样式和选择器之间的关系在某些方面比较紧密的时候,使用@extend。除此之外,你可以使用@mixin在任何地方。
5,控制指令
Sass 提供了一些基础的控制指令,比如在满足一定条件时引用样式,或者设定范围重复输出格式。控制指令是一种高级功能,日常编写过程中并不常用到,主要与混合指令 (mixin) 配合使用
@if
@if用于定义一些条件,当输入的值满足条件时做对应的事情 当@if 的表达式成立时,输出 {} 内的代码:
p{
@if 1 + 1 == 2 { width: 20px }
}
在这段代码中 1 + 1 == 2这个表达式无疑是成立的,通俗解释就是 1+1是否等于2 (中间的两个等号代表:是否等于 的意思) 这个表达式成立,那么后面 {}里的代码才会被输出 编译过后得到:
p{
width: 20px;
}
这里涉及到了Sass中的运算:
Sass中支持所有数字的加减乘除 以及取整等运算 其运算符分别是:
- 相加 +
- 相减 -
- 相乘 *
- 相除 /
- 取整 % 并且当两个值之间的单位不统一时会进行有限的单位转换,如:
p{
width: 2 * 20px;
}
编译成:
p{
width: 40px;
}
但是当两个值都带有单位,并且单位不统一的时候Sass就处理不了了,尽量不要写这样的代码:
p{
width: 2em * 20px;
}
另外,关系运算 <, >, <=, >= 也可用于数字运算,相等运算 ==, != 可用于所有数据类型。 因为”/“号有时候会用作分号而不是除号,所以用的时候要注意:
1.不相除
.main{
width: 120px/2;
}
编译过后:
.main{
width: 120px/2;
}
加括号可以解决问题:
.main{
width: (120px/2);
}
2.单位消除
.main{
width: (120px/2px);
}
编译过后:
.main{
width: 60;
}
解决方法:尽量不要让运算的两边都带单位
3.font复合样式:考虑到移动端布局需要替换单位,我们可能会这么写:
$rem: 30rem;
p{
font: 120px/$rem / 30px/$rem Arial;
}
首先,除号两边都带有单位,且不统一,会直接报错。所以变量的值里尽量不要带有单位
解决方法:
$rem: 30;
p{
font: #{(120rem/$rem)} / (30rem/$rem) Arial;
}
编译过后:
p {
font: 4rem / 1rem Arial;
}
以上代码中用到了插值语句: #{} 通过插值语句可以在选择器或属性名中使用变量:
$name: foo;
$attr: border;
.#{$name} {
#{$attr}-color: blue;
}
编译过后:
.foo {
border-color: blue;
}
可以在选择器名中使用变量,这将是一个非常好用的功能,以后会用到,现在继续讲控制指令:
@for 循环
@for 指令可以在限制的范围内重复输出格式,每次按要求(变量的值)对输出结果做出变动。这个指令包含两种格式:@for $var from through ,或者 @for $var from to ,区别在于 through 与 to 的含义:当使用 through 时,条件范围包含 与 的值,而使用 to 时条件范围只包含 的值不包含 的值。另外,$var 可以是任何变量,比如 $i; 和 必须是整数值。
比如:
@for $num from 1 through 3 {
.item{
width:$num * 3px;
}
}
编译过后:
.item {
width: 3px;
}
.item {
width: 6px;
}
.item {
width: 9px;
}
从以上代码可以看出 .item 被循环输出了 3次 。并且每一次 变量 num 变量用到选择器名称里面,这样的话就不至于输出的选择器是一样的了。还记得刚才在上面提到过的插值语句么?没错了,就是它:#{}
ok 现在我们利用@for一次性生成了3个选择器,当然我们可以生成更多,比如考虑以下案例:
.box{
width: 200px;
height: 200px;
border: 1px solid #899;
border-radius: 50%;
position: relative;
.item{
width: 2px;
height: 10px;
background: #899;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
@for $num from 1 through 12 {
.item:nth-child(#{$num}){
transform: rotate((360deg/12)*$num) translateY(-96px);
}
}
}
编译过后的代码大家可能也猜到了,所以也就不放在这里了
@each
@each 指令的格式是 $var in , $var 可以是任何变量名,比如 $length 或者 $name,而 是一连串的值,也就是值列表。
@each 将变量 $var 作用于值列表中的每一个项目,然后输出结果,例如:
@each $animal in puma, sea-slug, egret, salamander {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
}
}
编译后:
.puma-icon {
background-image: url('/images/puma.png'); }
.sea-slug-icon {
background-image: url('/images/sea-slug.png'); }
.egret-icon {
background-image: url('/images/egret.png'); }
.salamander-icon {
background-image: url('/images/salamander.png'); }
@while
@while 指令重复输出格式直到表达式返回结果为 false。这样可以实现比 @for 更复杂的循环,只是很少会用到。例如:
$i: 6;
@while $i > 0 {
.item-#{$i} { width: 2em * $i; }
$i: $i - 2;
}
编译后:
item-6 {
width: 12em; }
.item-4 {
width: 8em; }
.item-2 {
width: 4em; }
可以看到while的功能和 @for 差不多的,只是 @while可以根据一定的规则进行循环,而不是递增或者递减
6,其他
1,@import 说到这个,有同学可能有疑问,css本身也有@import属性,sass再提出来,会不会有所多余呢?我们来分析一下css的 @import
- 它更多是用来做媒体查询的
- 它每引入一个文件,都会向服务器发送一次请求
- 它并不是把引入的文件和当前文件进行融合
- 引入文件中定义的变量不能在当前文件中使用
Sass 拓展了 @import 的功能,允许其导入 SCSS 或 Sass 文件。被导入的文件将合并编译到同一个 CSS 文件中,另外,被导入的文件中所包含的变量或者混合指令 (mixin) 都可以在导入的文件中使用。不过Sass拓展的@import不能包含以下信息:
- 文件拓展名是.css
- 文件名以http://开头;
- 文件名是url();
- 不能包含媒体查询 media queries。
@import 可以导入拓展名是.scss 和.sass的文件。如果没有指定拓展名,Sass将会尝试寻找文件名相同,拓展名为.scss 或 .sass的文件并将其导入 比如
@import "style.scss"
或
@import "style"
以上两行代码都可以导入文件style.scss
另外,Sass可以把Sass文件当做一个组件引入而不会把这个组件单独编译成css文件 而想要实现这个功能只需要在文件名前面加上一个下划线就可以了 比如_components.scss 这时候编辑器不会自动编译这个文件,而只有当我们在另一个scss文件里引入这个文件之后,才会把这个文件里的内容编译到引入的文件里:
现在我在项目中建立了两个scss文件
- _components.scss
- style.scss
可以看到_components.scss并没有被编辑器所编译,只有一个孤零零的scss文件,而下面的style.scss文件则被编译成了css文件,并附带了一个map文件。
另外还可以看到一点,_components中定义的@mixin在style中是可以正常使用的,也就是说我们可以把一些公用的文件提取出来,然后在需要用的地方引入就可以了,这对于模块化开发非常有帮助,
打开bootstrap的源码,可以看到里面定义了非常多以下划线开头的scss文件,我们可以称这些文件为组件,每个组件只负责一个功能模块,然后在一个汇总的scss文件中引入就可以了,甚至组件之间可以相互引用相互依赖,这样我们最终只需要关心最终的scss文件就可以了。这种组件,Sass称之为 Partials。
甚至,在Sass中,@import还可以用于嵌套:
.example {
color: red;
}
#main {
@import "example";
}
编译之后:
#main .example {
color: red;
}
2,@media
Sass的嵌套体系中还有一个比较好用的东西 @media .这个指令的基本用法和css中的用法基本一样,只是增加了一点额外的功能:允许其在css规则中嵌套。如果@media 嵌套在css规则内,在编译时,@media将会被编译到文件的最外层,包含嵌套的父选择器。这个功能让 @media 用起来更方便,不需要重复使用选择器,也不会打乱css的书写流程。
.sidebar {
width: 300px;
@media screen and (orientation: landscape) {
width: 500px;
}
}
比如这段代码,从上往下来读我们能理解到的意思是: 选择器sidebar 宽度为 300 当媒询条件满足时,宽度为500 而实际编译完成之后也确实会编译成我理解的那样
.sidebar {
width: 300px; }
@media screen and (orientation: landscape) {
.sidebar {
width: 500px; }
}
甚至 @media 的 queries 允许互相嵌套使用,编译时,Sass 自动添加 and
@media screen {
.sidebar {
@media (orientation: landscape) {
width: 500px;
}
}
}
编译之后:
@media screen and (orientation: landscape) {
.sidebar {
width: 500px; }
}
最后
以上,就是我总结的关于sass的比较实用的内容啦,有什么问题欢迎大家吐槽