一、使用变量
sass 让人们受益的一个重要特性就是为 CSS 引入了变量。你可以把反复使用的 CSS 属性值 定义成变量,然后通过变量名来引用它们,而无需重复书写这一属性值。或者,对于仅使用过一 次的属性值,你可以赋予其一个易懂的变量名,让人一眼就知道这个属性值的用途。
sass 使用 $ 符号来标识变量(老版本是用的!),比如 $color-base 和 $nav-width。为什么选择 $ 符号呢?因为它好认、更具美感,且在 CSS 中并无他用,不会导致与现存或未来的 CSS 语法冲突。
1. 变量声明
sass 变量的声明和 css 属性的声明很像:
这意味着变量 $color-base 的值是 #00c16f,任何可以用作 CSS 属性值的赋值都可以用作 sass 的变量值,甚至是以空格分割的多个属性值:
或以逗号分割的多个属性值,如$plain-font: "Myriad Pro"、Myriad、"Helvetica Neue"、Helvetica、"Liberation Sans"、Arial和sans-serif; sans-serif;
。这时变 量还没有生效,除非你引用这个变量。
2.变量引用
凡是 CSS 属性的标准值(比如说1px或者bold)可存在的地方,变量就可以使用。CSS 生成时,变量会被它们的值所替代。如果你需要一个不同的值,只需要改变这个变量的值,则所有引用此变量的地方生成的值都会随之改变。
HTML结构
浏览器显示效果
看上边示例中的 $color-border 变量,它被直接赋值给 .bbb 的 border 属性,当这段代码被编译输出 CSS 时,$color-border 会被 red 这一颜色值所替代。产生的效果就是 .bbb 一条1像素宽、实心且颜色值为 red 的边框。
也可以在 $border-base 变量的声明中使用 $color-border 这个变量,产生的效果就跟你直接为 border 属性设置了一个 1px solid $color-border 的值是一样的。
3.变量的作用域
sass 中的变量与 css 属性不同,变量可以在 css 规则块定义之外存在。如上例子,这些就是定义在规则块外的变量:
在这个样式表中,规则块都可以引用它。
当变量定义在 CSS 规则块内,那么该变量只能在此规则块内使用。
我在 .aaa 里定义了 $c:center,在 .aaa 规则块中可以使用,.bbb 和 .ccc 也包含在它的规则块内,所以也能使用。但是在 .abc 中,就使用不了 .aaa 中规则块的东西,因为 .aaa 和 .abc 是同级关系。
这里要注意一点,在定义变量的时候,要把变量定义在使用之前,如下情况就会报错:
当 align-items 使用 $c 的时候,$c 还没有定义
报错内容是 $c 未定义。不只是规则块内的变量,规则块外的变量也是一样,所以推荐把变量定义在最前面。
4.变量名用中划线还是下划线分隔
$color_base和$color-base其实指向的是同一个变量。
其实在 sass 的大多数地方,中划线命名的内容和下划线命名的内容是互通的,除了变量,也包括对混合器和 Sass 函数的命名。但是在 sass 中纯 css 部分不互通,比如类名、ID或属性名。
二.嵌套CSS规则
其实在最开始变量引用的地方我就使用了嵌套 css 规则。
CSS 中重复写选择器是非常恼人的。如果要写一大串指向页面中同一块的样式时,往往需要 一遍又一遍地写同一个 ID 或类名:
.aaa div{ width:100px; height:100px; background:red; }
.aaa div h1{ color:#999; font-size:50px;}
.aaa div p{ margin-top:20px;}
.aaa input{ margin-left:20px;}
像这种情况,sass 可以让你只写一遍,且使样式可读性更高。在Sass中,你可以像俄罗斯套娃那样在规则块中嵌套规则块。sass 在输出 css 时会帮你把这些嵌套规则处理好,避免你的重复书写。
上边的例子,会在输出css
时把它转换成跟你之前看到的一样的效果。这个过程中,sass
用了两步,每一步都是像打开俄罗斯套娃那样把里边的嵌套规则块一个个打开。首先,把.aaa(父级)这个class放到div选择器(子级)和input选择器(子级)的前边。
然后,.aaa div里边还有嵌套的规则,sass
重复一遍上边的步骤,把新的选择器添加到内嵌的选择器前边。
一个给定的规则块,既可以像普通的CSS那样包含属性,又可以嵌套其他规则块。当你同时要为一个容器元素及其子元素编写特定样式时,这种能力就非常有用了。
大多数情况下这种简单的嵌套都没问题,但是有些场景下不行,比如你想要在嵌套的选择器里边立刻应用一个类似于:hover
的伪类。为了解决这种以及其他情况,sass
提供了一个特殊结构 & 。
1.父选择器的标识符&
一般情况下,sass
在解开一个嵌套规则时就会把父选择器(.aaa)通过一个空格连接到子选择器的前边(div
和input
)形成(.aaa div 和.aaa input)。
这种在CSS里边被称为后代选择器,因为它选择 class 为 aaa 的元素内所有命中选择器 div 和 input 的元素。但在有些情况下你却不会希望sass
使用这种后代选择器的方式生成这种连接。
最常见的一种情况是当你为链接之类的元素写:hover
这种伪类时,你并不希望以后代选择器的方式连接。比如说,下面这种情况sass
就无法正常工作:
.aaa div{
background:red;
:hover{ background:blue;}
}
:hover{ background:blue;} 没有效果,其实只要在其前面加上&就可以了,
.aaa div{
background:red;
&:hover{ background:blue;}
}
当包含父选择器标识符的嵌套规则被打开时,它不会像后代选择器那样进行拼接,而是&被父选择器直接替换:
.aaa div{ background:red;}
.aaa div:hover{ background:blue;}
在为父级选择器添加:hover
等伪类时,这种方式非常有用。
同时父选择器标识符还有另外一种用法,你可以在父选择器之前添加选择器,下面会有一个例子。
sass
在选择器嵌套上是非常智能的,即使是带有父选择器的情况。当sass
遇到群组选择器(由多个逗号分隔开的选择器形成)也能完美地处理这种嵌套。
2.群组选择器的嵌套
如果需要在一个特定的容器元素内对这样一个群组选择器进行修饰。css
的写法会让你在群组选择器中的每一个选择器前都重复一遍容器元素的选择器。
非常幸运,sass
的嵌套特性在这种场景下也非常有用。当sass
解开一个群组选择器规则内嵌的规则时,它会把每一个内嵌选择器的规则都正确地解出来:
首先sass
将.aaa
和h1
.aaa
和h2
.aaa
和h3
分别组合,然后将三 者重新组合成一个群组选择器,生成你前边看到的普通css
样式。对于内嵌在群组选择器内的嵌套规则,处理方式也一样:
首先sass
将 .bbb 和 .ccc 和 h2 分别组合,然后将二者重新组合成一个群组选择器。
处理这种群组选择器规则嵌套上的强大能力,正是sass
在减少重复敲写方面的贡献之一。尤其在当嵌套级别达到两层甚至三层以上时,与普通的css
编写方式相比,只写一遍群组选择器大大减少了工作量。
有利必有弊,你需要特别注意群组选择器的规则嵌套生成的css
。虽然sass
让你的样式表看上去很小,但实际生成的css
却可能非常大,这会降低网站的速度。
3.子组合选择器和同层组合选择器:>、+ 和 ~
这些组合选择器可以毫不费力地应用到sass
的规则嵌套中。可以把它们放在外层选择器后边,或里层选择器前边:
sass
会如你所愿地将这些嵌套规则一一解开组合在一起:
在sass
中,不仅仅css
规则可以嵌套,对属性进行嵌套也可以减少很多重复性的工作。
4.嵌套属性
在sass
中,除了CSS选择器,属性也可以进行嵌套。尽管编写属性涉及的重复不像编写选择器那么糟糕,但是要反复写border-style
border-width
border-color
以及border-*
等也是非常烦人的。在sass
中,你只需敲写一遍border
:
嵌套属性的规则是这样的:把属性名从中划线-的地方断开,在根属性后边添加一个冒号:,紧跟一个{ }
块,把子属性部分写在这个{ }
块中。
就像css
选择器嵌套一样,sass
会把你的子属性一一解开,把根属性和子属性部分通过中划线-连接起来,最后生成的效果与你手动一遍遍写的css
样式一样:
对于属性的缩写形式,你甚至可以像下边这样来嵌套,指明例外规则:
这里有个小坑,border冒号后面要加空格,不然会报错,说border:后的css无效。
这比下边这种同等样式的写法要好:
属性和选择器嵌套是非常伟大的特性,因为它们不仅大大减少了你的编写量,而且通过视觉上的缩进使你编写的样式结构更加清晰,更易于阅读和开发。
即便如此,随着你的样式表变得越来越大,这种写法也很难保持结构清晰。有时,处理这种大量样式的唯一方法就是把它们分拆到多个文件中。sass
通过对css
原有@import
规则的改进直接支持了这一特性。
三. 导入SASS文件
css
有一个特别不常用的特性,即@import
规则,它允许在一个css
文件中导入其他css
文件。然而,后果是只有执行到@import
时,浏览器才会去下载其他css
文件,这导致页面加载起来特别慢。
sass
也有一个@import
规则,但不同的是,sass
的@import
规则在生成css
文件时就把相关文件导入进来。这意味着所有相关的样式被归纳到了同一个css
文件中,而无需发起额外的下载请求。
另外,所有在被导入文件中定义的变量和混合器(下面会介绍)均可在导入文件中使用。
使用sass
的@import
规则并不需要指明被导入文件的全名。你可以省略.sass
或.scss
文件后缀(见下图)。这样,在不修改样式表的前提下,你完全可以随意修改你或别人写的被导入的sass
样式文件语法,在sass
和scss
语法之间随意切换。
举例来说,@import
"sidebar";这条命令将把sidebar.scss
文件中所有样式添加到当前样式表中。
SCSS 是 Sass 3 引入新的语法,其语法完全兼容 CSS3,并且继承了 Sass 的强大功能。也就是说,任何标准的 CSS3 样式表都是具有相同语义的有效的 SCSS 文件。
由于 SCSS 是 CSS 的扩展,因此,所有在 CSS 中正常工作的代码也能在 SCSS 中正常工作。也就是说,对于一个 Sass 用户,只需要理解 Sass 扩展部分如何工作的,就能完全理解 SCSS。
大部分扩展,例如变量、parent references 和 指令都是一致的;唯一不同的是,SCSS 需要使用分号和花括号而不是换行和缩进。例如,以下这段简单的 Sass 代码:
只需添加花括号和分号就能转换为 SCSS 语法:
1.使用SASS部分文件
当通过@import
把sass
样式分散到多个文件时,你通常只想生成少数几个css
文件。那些专门为@import
命令而编写的sass
文件,并不需要生成对应的独立css
文件,这样的sass
文件称为局部文件。
对此,sass
有一个特殊的约定来命名这些文件。此约定即,sass
局部文件的文件名以下划线开头。这样,sass
就不会在编译时单独编译这个文件输出css
,而只把这个文件用作导入。
当你@import
一个局部文件时,还可以不写文件的全名,即省略文件名开头的下划线。举例来说,你想导入themes/_night-sky.scss
这个局部文件里的变量,你只需在样式表中写@import
"themes/night-sky";
。
后面要加分号。局部文件可以被多个不同的文件引用。当一些样式需要在多个页面甚至多个项目中使用时,这非常有用。
我在写demo中是把scss文件引在script文件中:
放在export default上面,没有@,文件名要写全,加上文件类型后缀.scss,文件地址后面也没有任何符号。有个弊端,被导入文件中的混合器在导入文件中使不了。
2. 默认变量值
一般情况下,你反复声明一个变量,只有最后一处声明有效且它会覆盖前边的值。如下:
在上边的例子中,超链接的color
会被设置为red。
假如你写了一个可被他人通过@import
导入的sass
库文件,你可能希望导入者可以定制修改sass
库文件中的某些值。使用sass
的!default
标签可以实现这个目的。
它很像css
属性中!important
标签的对立面,不同的是!default
用于变量,含义是:如果这个变量被声明赋值了,那就用它声明的值,否则就用这个默认值。
在上例中,如果用户在导入你的sass
局部文件之前声明了一个 $font-size-base 变量,那么你的局部文件中对 $font-size-base 赋值20px
的操作就无效。如果用户没有做这样的声明,则 $font-size-base 将默认为20px
。
3.嵌套导入
跟原生的css
不同,sass
允许@import
命令写在css
规则内。这种导入方式下,生成对应的css
文件时,局部文件会被直接插入到css
规则内导入它的地方。举例说明,有一个名为_blue-theme.scss
的局部文件,内容如下:
然后把它导入到一个CSS规则内,如下所示:
被导入的局部文件中定义的所有变量和混合器,也会在这个规则范围内生效。这些变量和混合器不会全局有效,这样我们就可以通过嵌套导入只对站点中某一特定区域运用某种颜色主题或其他通过变量配置的样式。
4.原生的CSS导入
由于sass
兼容原生的css
,所以它也支持原生的CSS@import
。尽管通常在sass
中使用@import
时,sass
会尝试找到对应的sass
文件并导入进来,但在下列三种情况下会生成原生的CSS@import
,尽管这会造成浏览器解析css
时的额外下载:
-
- 被导入文件的名字以
.css
结尾; - 被导入文件的名字是一个URL地址(比如http://www.sass.hk/css/css.css),由此可用谷歌字体API提供的相应服务;
- 被导入文件的名字是
CSS
的url()值。
- 被导入文件的名字以
这就是说,你不能用sass
的@import
直接导入一个原始的css
文件,因为sass
会认为你想用css
原生的@import
。但是,因为sass
的语法完全兼容css
,所以你可以把原始的css
文件改名为.scss
后缀,即可直接导入了。
文件导入是保证sass
的代码可维护性和可读性的重要一环。次之但亦非常重要的就是注释了。注释可以帮助样式作者记录写sass
的过程中的想法。
在原生的css
中,注释对于其他人是直接可见的,但sass
提供了一种方式可在生成的css
文件中按需抹掉相应的注释。
四.静默注释
css
中注释的作用包括帮助你组织样式、以后你看自己的代码时明白为什么这样写,以及简单的样式说明。但是,你并不希望每个浏览网站源码的人都能看到所有注释。
sass
另外提供了一种不同于css
标准注释格式/* ... */
的注释语法,即静默注释,其内容不会出现在生成的css
文件中。静默注释的语法跟JavaScript
Java
等类C
的语言中单行注释的语法相同,它们以//
开头,注释内容直到行末。
实际上,css
的标准注释格式/* ... */
内的注释内容亦可在生成的css
文件中抹去。当注释出现在原生css
不允许的地方,如在css
属性或选择器中,sass
将不知如何将其生成到对应css
文件中的相应位置,于是这些注释被抹掉。
你已经掌握了sass
的静默注释,了解了保持sass
条理性和可读性的最基本的三个方法:嵌套、导入和注释。现在,我们要进一步学习新特性,这样我们不但能保持条理性还能写出更好的样式。首先要介绍的内容是:使用混合器抽象你的相关样式。
五.混合器
如果你的整个网站中有几处小小的样式类似(例如一致的颜色和字体),那么使用变量来统一处理这种情况是非常不错的选择。但是当你的样式变得越来越复杂,你需要大段大段的重用样式的代码,独立的变量就没办法应付这种情况了。
你可以通过sass
的混合器实现大段样式的重用。混合器使用@mixin
标识符定义。这个标识符给一大段样式赋予一个名字,这样你就可以轻易地通过引用这个名字重用这段样式。下边的这段sass
代码,定义了一个非常简单的混合器:
就像定义了一个对象,然后在规则块内引用一样。
然后就可以在你的样式表中通过@include
来使用这个混合器,放在你希望的任何地方。@include
调用会把混合器中的所有样式提取出来放在@include
被调用的地方。如果像下边这样写:
sass最终生成:
1.何时使用混合器
利用混合器,可以很容易地在样式表的不同地方共享样式。如果你发现自己在不停地重复一段样式,那就应该把这段样式构造成优良的混合器,尤其是这段样式本身就是一个逻辑单元,比如说是一组放在一起有意义的属性。
判断一组属性是否应该组合成一个混合器,一条经验法则就是你能否为这个混合器想出一个好的名字。如果你能找到一个很好的短名字来描述这些属性修饰的样式,
比如rounded-corners
fancy-font
或者no-bullets
,那么往往能够构造一个合适的混合器。如果你找不到,这时候构造一个混合器可能并不合适。
混合器在某些方面跟css
类很像。都是让你给一大段样式命名,所以在选择使用哪个的时候可能会产生疑惑。最重要的区别就是类名是在html
文件中应用的,而混合器是在样式表中应用的。
这就意味着类名具有语义化含义,而不仅仅是一种展示性的描述:用来描述html
元素的含义而不是html
元素的外观。而另一方面,混合器是展示性的描述,用来描述一条css
规则应用之后会产生怎样的效果。
因为使用语义化的类名亦可以帮你避免重复使用混合器。为了保持你的html
和css
的易读性和可维护性,在写样式的过程中一定要铭记二者的区别。
2.混合器中的CSS规则
混合器中不仅可以包含属性,也可以包含css
规则,包含选择器和选择器中的属性。
当一个包含css
规则的混合器通过@include
包含在一个父规则中时,在混合器中的规则最终会生成父规则中的嵌套规则。
混合器中的规则甚至可以使用sass
的父选择器标识符&
。使用起来跟不用混合器时一样,sass
解开嵌套规则时,用父规则中的选择器替代&
。
3.给混合器传参
混合器并不一定总得生成相同的样式。可以通过在@include
混合器时给混合器传参,来定制混合器生成的精确样式。当@include
混合器时,参数其实就是可以赋值给css
属性值的变量。如果你写过JavaScript
,这种方式跟JavaScript
的function
很像:
当混合器被@include
时,你可以把它当作一个css
函数来传参。如果你像下边这样写:
也可以传很多参数:
当你@include混合器时,有时候可能会很难区分每个参数是什么意思,参数之间是一个什么样的顺序。为了解决这个问题,sass
允许通过语法$name: value
的形式指定每个参数的值。
这种形式的传参,参数顺序就不必再在乎了,只需要保证没有漏掉参数即可:
尽管给混合器加参数来实现定制很好,但是有时有些参数我们没有定制的需要,这时候也需要赋值一个变量就变成很痛苦的事情了。所以sass
允许混合器声明时给参数赋默认值。
4.默认参数值
为了在@include
混合器时不必传入所有的参数,我们可以给参数指定一个默认值。参数默认值使用$name: default-value
的声明形式,默认值可以是任何有效的css
属性值,甚至是其他参数的引用,如下代码:
如果只调用了@include discoloration ( blue ),$hover也会被自动赋值为blue。
六.选择器继承
使用sass
的时候,最后一个减少重复的主要特性就是选择器继承。选择器继承是说一个选择器可以继承为另一个选择器定义的所有样式。这个通过@extend
语法实现,如下代码:
.aaaa
不仅会继承.aaa
自身的所有样式,任何跟.aaa
有关的组合选择器样式也会被.aaaa
以组合选择器的形式继承。
学到的新知识:
元素.类名
例如:p.abc{
color:red;
}
类名是abc的标签p的颜色是red
类名.元素
.abc p{
color:red;
}
表示:类名abc的任何标签旗下的p标签颜色是red
1.何时使用继承
前面介绍了混合器主要用于展示性样式的重用,而类名用于语义化样式的重用。因为继承是基于类的(有时是基于其他类型的选择器),所以继承应该是建立在语义化的关系上。
当一个元素拥有的类(比如说.seriousError
)表明它属于另一个类(比如说.error
),这时使用继承再合适不过了。
这有点抽象,所以我们从几个方面来阐释一下。想象一下你正在编写一个页面,给html
元素添加类名,你发现你的某个类(比如说.seriousError
)另一个类(比如说.error
)的细化。你会怎么做?
-
- 你可以为这两个类分别写相同的样式,但是如果有大量的重复怎么办?使用
sass
时,我们提倡的就是不要做重复的工作。 - 你可以使用一个选择器组(比如说
.error
.seriousError
)给这两个选择器写相同的样式。如果.error的所有样式都在同一个地方,这种做法很好,但是如果是分散在样式表的不同地方呢?再这样做就困难多了。
- 你可以为这两个类分别写相同的样式,但是如果有大量的重复怎么办?使用
-
-
- 你可以使用一个混合器为这两个类提供相同的样式,但当
.error
的样式修饰遍布样式表中各处时,这种做法面临着跟使用选择器组一样的问题。这两个类也不是恰好有相同的 样式。你应该更清晰地表达这种关系。 - 综上所述你应该使用
@extend
。让.seriousError
从.error
继承样式,使两者之间的关系非常清晰。更重要的是无论你在样式表的哪里使用.error
.seriousError
都会继承其中的样式。
- 你可以使用一个混合器为这两个类提供相同的样式,但当
-
2.继承的工作细节
跟变量和混合器不同,继承不是仅仅用css
样式替换@extend处的代码那么简单。为了不让你对生成的css
感觉奇怪,对这背后的工作原理有一定了解是非常重要的。
@extend
背后最基本的想法是,如果.seriousError @extend .error
, 那么样式表中的任何一处.error
都用.error
.seriousError
这一选择器组进行替换。
这就意味着相关样式会如预期那样应用到.error
和.seriousError
。当.error
出现在复杂的选择器中,比如说h1.error
.error a
或者#main .sidebar input.error[type="text"]
,那情况就变得复杂多了,但是不用担心,sass
已经为你考虑到了这些。
关于@extend
有两个要点你应该知道。
-
- 跟混合器相比,继承生成的
css
代码相对更少。因为继承仅仅是重复选择器,而不会重复属性,所以使用继承往往比混合器生成的css
体积更小。如果你非常关心你站点的速度,请牢记这一点。 - 继承遵从
css
层叠的规则。当两个不同的css
规则应用到同一个html
元素上时,并且这两个不同的css
规则对同一属性的修饰存在不同的值,css
层叠规则会决定应用哪个样式。相当直观:通常权重更高的选择器胜出,如果权重相同,定义在后边的规则胜出。
- 跟混合器相比,继承生成的
混合器本身不会引起css
层叠的问题,因为混合器把样式直接放到了css
规则中,而继承存在样式层叠的问题。被继承的样式会保持原有定义位置和选择器权重不变。
不要用后代选择器去继承。
七. 小结
本文介绍了sass
最基本部分,你可以轻松地使用sass
编写清晰、无冗余、语义化的css
。对于sass
提供的工具你已经有了一个比较深入的了解,同时也掌握了何时使用这些工具的指导原则。
变量是sass
提供的最基本的工具。通过变量可以让独立的css
值变得可重用,无论是在一条单独的规则范围内还是在整个样式表中。变量、混合器的命名甚至sass
的文件名,可以互换通用_
和-
。同样基础的是sass
的嵌套机制。嵌套允许css
规则内嵌套css
规则,减
少重复编写常用的选择器,同时让样式表的结构一眼望去更加清晰。sass
同时提供了特殊的父选择器标识符&
,通过它可以构造出更高效的嵌套。
你也已经学到了sass
的另一个重要特性,样式导入。通过样式导入可以把分散在多个sass
文件中的内容合并生成到一个css
文件,避免了项目中有大量的css
文件通过原生的css
@import
带来的性能问题。通过嵌套导入和默认变量值,导入可以构建更强有力的、
可定制的样式。混合器允许用户编写语义化样式的同时避免视觉层面上样式的重复。你不仅学到了如何使用混合器减少重复,同时学习到了如何使用混合器让你的css
变得更加可维护和语义化。最后,我们学习了与混合器相辅相成的选择器继承。
继承允许你声明类之间语义化的关系,通过这些关系可以保持你的css
的整洁和可维护性。