Sass与Compress实战:第二章

时间:2023-03-09 15:27:11
Sass与Compress实战:第二章

1.使用变量

  Sass使用$符号来标识变量,比如$highlight-color。

1.1声明变量

  Sass声明变量和CSS声明属性很像:

$highlight-color : #abcdef;

  这意味着变量$highlight-color现在的值时#abcdef。任何可以用作CSS属性值的赋值都可以用作Sass的变量值,甚至是以空格分割的多个属性值,如$basic-border : 1px solid black;或以逗号分割的多个属性值,如$plain-font : "Myriad Pro"、Myriad、"Helvetica Neue"、Helvetica。只有当引用了变量时它才会生效。

  与CSS属性不同,变量可以在CSS规则块定义之外存在。当变量定义在CSS规则块内,那么该变量只能在此规则块内使用。例如:

$nav-color: #abcdef;
nav {
$width: 100px;
width: $width;
color: $nav-color;
}

  在这段代码中,变量$nav-color定义在规则块外部,因此这个样式表中的任意位置都可以引用这个变量,而$width定义在了规则块内部,因此只有在nav的规则块内部才可以引用它。也就意味着你可以在样式表的其他地方定义和引用$width不会影响到nav的规则块内部的$width变量。

1.2变量引用

  凡是CSS属性的标准值(比如说1px或者bold)可存在的地方,变量就可以使用。CSS生成时,变量会被它们的值所替代,之后,如果你需要一个不同的值,只需要改变这个变量的值,则所有引用到这个变量的地方生成的值都会被改变。

  在声明变量时,变量值也可以引用其他变量。下例在独立的颜色值粒度上定义了一个变量,且在另一个更复杂的边框值粒度上也定义了一个变量:

$highlight-color : #abcdef;
$highlight-border : 1px $highlight-color solid ; .selected {
border : $highlight-border;
}

  这里,$highlight-border变量的声明中使用了$highlight-color这个变量。产生的效果就跟你直接为border属性设置了一个1px $highlight-color solid的值是一样的。

1.3变量名用中划线还是下划线分隔

  Sass的变量名可以与CSS中的属性名和选择器名称相同,包括中划线和下划线。这两种方法互相兼容,用中划线声明的变量可以用下划线的方式引用,例如:

$link-color : blue;

a {
color : $link_color;
}

$link-color 和 $link_color其实指向的是同一个变量。除了变量,混合器和Sass函数的中划线命名内容和下划线命名内容也是互通的。但在Sass中纯CSS部分不通,比如类名、ID或属性名。

2.嵌套CSS规则

  Sass的嵌套规则能够避免重复书写CSS选择器。假设有这么一段代码:

#content article h1 { color : #333 }
#content article p { margin-bottom : 1.4em }
#content aside { background-color : #eee }

  Sass文件代码:

#content {
article {
h1 { color : #333 }
p { margin-bottom : 1.4em }
}
aside { background-color : #eee }
}

  上边的例子,Sass会在输出CSS时把它转换成跟你之前看到的一样的效果。在这个过程中,Sass用了两步:首先,把#content(父级)这个ID放到article(子级)选择器和aside(子级)选择器的前边:

#content article {
h1 { color : #333 }
p { margin-bottom : 1.4em }
}
#content aside { background-color : #eee }

  然后,#content article里面还有嵌套的规则,Sass重复一遍上边的步骤,把新的选择器添加到内嵌的选择器前边:

#content article h1 { color : #333 }
#content article p { margin-bottom : 1.4em }
#content aside { background-color : #eee }

  一个给定的规则块,既可以像普通的CSS那样包含属性,又可以嵌套其他规则块。当你同时要为一个容器元素及其子元素编写特定样式时,这种能力就非常有用:

#content {
background-color : #f5f5f5;
aside { background-color : #eee }
}

  容器元素的样式规则会被单独抽离出来,而嵌套元素的样式规则会像容器元素没有包含任何属性时那样抽离出来。

编译后的CSS:

#content { background-color : #f5f5f5 }
#content aside { background-color : #eee }

  假如你要在嵌套的选择器中应用一个类似于:hover的伪类,就无法使用这种嵌套。为了解决这种情况,Sass提供一个特殊的结构 & 。

2.1 父选择器的标识符 & 

第一种用法:为元素添加伪类时

article a {
color : blue;
& : hover { color : red }
}

编译后的CSS:

article a { color : blue }
article a:hover { color : red }

第二种用法:在父选择器之前添加选择器

  举例来说,当用户在使用IE浏览器时,会通过JavaScript在<body>标签上添加一个ie的类名,为这种情况编写特殊的样式如下:

#content aside {
color : red;
body.ie & { color : green }
}

  Sass在选择器嵌套上是非常智能的,即使是带有父选择器的情况,当Sass遇到群组选择器也能完美的处理这种嵌套。

2.2 群组选择器嵌套

  CSS的群组选择器会让你在群组选择器中的每一个选择器前都重复一遍容器元素的选择器。

.container h1 , .container h2 , .container h3 { margin-bottom : .8em }

  当Sass解开一个群组选择器规则内嵌的规则时,它会把每一个内嵌选择器的规则都正确地解出来:

.container {
h1 , h2 , h3 { margin-bottom : .8em }
}

  首先Sass将.container和h1、.container和h2、.container和h3分别组合,然后将三者重新组合成一个群组选择器,生成上述普通CSS样式。对于内嵌在群组选择器内的嵌套规则,处理方式也一样:

nav , aside {
a { color : blue }
}

  首先Sass将nav和a、aside和a分别组合,然后将二者重新组合成一个群组选择器:

nav a , aside a { color : blue }

  需要特别注意群组选择器的规则嵌套生成的CSS,虽然Sass让你的样式表看上去很小,但实际生成的CSS却可能非常大。

2.3 子组合选择器和同层组合选择器:>、+和~

  这三个组合选择器必须和其他选择器配合使用,以指定浏览器仅选择某种特定上下文中的元素。

例如:使用子组和选择器">"选择一个元素的直接子元素:

article > section { border : 1px solid #ccc }

例如:使用同层相邻组合选择器 " + " 来选择header元素后紧跟的p元素:

header + p { font-size : 1.1em }

例如:使用同层全体组合选择器 " ~ "来选择article后的同层article元素,不管它们之间隔了多少其他元素:

article ~ article { border-top : 1px dashed #ccc }

  这些组合选择器可以毫不费力地应用到Sass的规则嵌套中。可以把它们放在外层选择器后边,或里层选择器前边:

article {
~ article { border-top : 1px dashed #ccc }
> section { background : #eee }
dl > {
dt { color : #333 }
dd { color : #555 }
}
nav + & { margin-top : 0 }
}

Sass会如你所愿地将这些嵌套规则一一解开组合:

article ~ article { border-top : 1px dashed #ccc }
article > footer { background : #eee }
article dl > dt { color : #333 }
article dl > dd { color : #555 }
nav + article { margin-top : 0 }

  在Sass中不仅仅CSS规则可以嵌套,对属性进行嵌套也可以减少很多重复的工作。

2.4 嵌套属性

  尽管编写属性涉及的重复不像编写选择器那么糟糕,但是要反复写border-style、border-width、border-color以及border-*等也是非常烦人的。在Sass中,你只需要敲一遍border:

nav {
border: {
style : solid;
width : 1px;
color : #ccc;
}
}

  嵌套属性的规则:把属性名从中划线 "-" 的地方断开,在根属性后面添加一个冒号:,紧跟一个{}块,把子属性部分写在这个{}块中。就像CSS选择器嵌套一样,Sass会把你的子属性一一解开,把根属性和子属性部分通过中划线 "-" 连接起来,最后生成的效果与你手动一遍遍写的CSS样式一样:

nav {
border-style : solid;
border-width : 1px;
border-color : #ccc;
}

  对于属性的缩写形式,你甚至可以像下边这样来嵌套,指明例外的规则:

nav {
border : 1px solid #ccc {
left : 0px;
right : 0px;
}
}

这比下面这种同等样式的写法要好:

nav {
border : 1px solid #ccc;
border-left : 0px;
border-right : 0px;
}

  虽然属性和选择器嵌套是强大的特性但是随着样式表的增大,这种写法也难以保持结构清晰。有时,处理这种大量样式的唯一方法就是把它们拆分到多个文件中,Sass通过对CSS原有@import规则的改进直接支持了这一特性。

3.导入Sass文件

  CSS有一个特别不常用的特性,即@import规则,它允许在一个CSS文件中导入其他CSS文件。然而,后果是只有执行到@import时,浏览器才会去下载其他CSS文件,这导致页面加载起来特别慢。

  Sass也有一个@import规则,但不同的是,Sass的@import规则在生成CSS文件时就把相关文件导入进来。这意味着所有相关的样式被归纳到了同一个CSS文件中,而无需发起额外的下载请求。另外,所有被导入文件中定义的变量和混合器均可在导入文件中使用。

  使用Sass的@import规则并不需要指明被导入文件的全名,可以省略后缀。这样,可以在不修改样式表的前提下,你完全可以随意修改你或别人写的被导入的Sass样式文件语法,在Sass和Scss语法之间随意切换。举例来说,@import "sidebar"; 这条命令将把sidebar.scss文件中所有样式添加到当前样式表中。

3.1 使用Sass部分文件

  专门为@import命令而编写的Sass文件,并不需要生成对应的独立CSS文件,这样的Sass文件称为局部文件。对此,Sass约定局部文件的文件名以下划线开头。这样,Sass就不会在编译时单独编译这个文件输出CSS,而只把这个文件用作导入。

  当你@import一个局部文件时,还可以不写文件的全名,即省略文件名开头的下划线。举例来说,你想导入themes/_night-sky.scss这个局部文件里的变量,你只需要在样式表中写@import "themes/night-sky" ;。

  局部文件可以被多个不同的文件引用。当一些样式需要在多个页面甚至多个项目中使用时,这非常有用。在这种情况下,有时需要在你的样式表中对导入的样式稍作修改,Sass有一个功能刚好可以解决这个问题:默认变量值

3.2 默认变量值

  假如你写了一个可被他人通过@import导入的Sass库文件,你可能希望导入者可定制修改Sass库文件中的某些值。使用Sass的!default标签可以实现这个目的,含义是:如果这个变量被声明赋值了,就用它声明的值,否则就用这个默认值。 

$fancybox-width : 400px !default;

.fancybox {
width : $fancybox-width
}

  在上例中,如果用户在导入你的Sass局部文件之前声明了一个$fancybox-width变量,那么你的局部文件中对$fancybox-width赋值400px的操作就无效。如果用户没有做这样的声明,则$fancybox-width将默认为400px。

3.3 嵌套导入

  Sass允许@import命令写在CSS规则内。这种导入方式下,生成对应的CSS文件时,局部文件会被直接插入到CSS规则内导入它的地方。举例说明,有一个名为_blue-theme.scss的局部文件,内容如下:

aside {
background : blue;
color : white;
}

然后把它导入到一个CSS规则内,如下所示:

.blue-theme { @import "blue-theme" }

生成的结果跟你直接在 .blue-theme 选择器内写 _blue-theme.scss文件的内容完全一样。

.blue-theme {
aside {
background : blue;
color : #fff;
}
}

  被导入的局部文件中定义的所有变量和混合器,也会在这个规则范围内生效。这些变量和混合器不会全局有效,这样我们就可以通过嵌套导入只对站点中某一特定区域运用某种颜色主题或其他通过变量配置的样式。

3.4 原生的CSS导入

  由于Sass兼容原生的CSS,所以它也支持原生的CSS@import。尽管通常在Sass中使用@import时,Sass会尝试找到对应的Sass文件并导入进来,但在下列三种情况下会生成原生的CSS@import,尽管这会造成浏览器解析CSS时的额外下载:

  ▶ 被导入文件的名字以 .css结尾;

  ▶ 被导入文件的名字是一个URL地址(比如 http://sass-lang.com/stylesheets/application.css),由此可用谷歌字体API提供的相应服务;

  ▶ 被导入文件的名字是CSS的URL()值。

  假如你使用Sass直接导入一个原始的CSS文件,Sass会认为你想用CSS原生的@import,所以你需要把原始的CSS文件名改为 .scss后缀,即可直接导入了。

  文件导入是保证Sass代码可维护性和可读性的重要一环。

4. 静默注释

  Sass提供一种注释语法,其内容不会出现在生成的CSS文件中,即静默注释。

body {
color : #333; // 这种注释内容不会出现在生成的CSS文件中
padding :; /* 这种注释内容会出现在生成的CSS文件中 */
}

  实际上,CSS的标准注释格式/* ... */内的注释内容也可以在生成的CSS文件中抹去。当注释出现在原生CSS不允许的地方,如在CSS属性或者选择器中,Sass将不知如何将其生成到对应CSS文件中的相应位置,于是这些注释被抹掉。 

body {
color /* 这块注释内容不会出现在生成的CSS中 */ : #333;
padding : 1em; /* 这块注释内容也不会出现在生成的CSS中*/ 0; }

总结:保持Sass条理性和可读性的最基本三个方法:嵌套、导入和注释。

2.5 混合器

  混合器使用@mixin标识符定义。这个标识符给一大段样式赋予一个名字,这样你就可以轻易地通过引用这个名字重复这段样式。

例如:下面这段Sass代码,定义了一个非常简单的混合器,目的是添加跨浏览器的圆边角框:

@mixin rounded-corners {
-moz-border-radius : 5px;
-webkit-border-radius : 5px;
border-radius : 5px;
}

  然后就可以在你的样式表中通过@include来使用这个混合器,放在你希望的任何地方。@include调用会把混合器中的所有样式提取出来放在@include被调用的地方。如果像下面这样写:

.notice {
background-color : green;
border : 2px solid #00aa00;
@include rounded-corners;
}

Sass最终生成:

.notice {
background-color : green;
border : 2px solid #00aa00;
-moz-border-radius : 5px;
-webkit-border-radius : 5px;
border-radius : 5px;
}

  混合器的功能很强大,可是大量的重用可能会导致生成的样式表过大,因此下面将讨论使用混合器的场景,避免滥用。

5.1 何时使用混合器

  如果你发现自己在不停的重复一段样式,那就应该把这段样式构造成优良的混合器,尤其是这段样式本身是一个逻辑单元,比如说是一组放在一起的有意义的属性。

  判断一组属性是否应该组合成一个混合器,一条经验法则就是你能否为这个混合器相出一个好 的名字。比如rounded-corners、fancy-font或者no-bullets,那么往往能构造一个合适的混合器。如果找不到,这时候构造一个混合器可能并不合适。

  混合器在某些方面跟CSS类很像。最重要的区别就是类名是在HTML文件中应用的,而混合器是在样式表中应用的。混合器是展示性的描述,用来描述一条CSS规则应用之后会产生怎样的效果。

  如果一个HTML元素有一个notice的类名,就表明了这个HTML元素的用途:向用户展示提醒信息。因为 .notice是一个有语义的类名。rounded-corners混合器是展示性的,它描述了包含它的CSS规则最终的视觉样式,尤其是边框角的视觉样式。

  混合器和类配合使用写出整洁的HTML和CSS,因为使用语义化的类名亦可帮你避免重复使用混合器。为了保持你的HTML和CSS的易读性和可维护性,在写样式的过程中一定要铭记二者的区别。

5.2 混合器中的CSS规则

  混合器中不仅可以包含属性,还可以包含CSS规则,包含选择器和选择器中的属性,如下代码所示:

@mixin no-bullets {
list-style : none;
li {
list-style-image : none;
list-style-type : none;
margin-left : 0px;
}
}

  当一个包含CSS规则的混合器通过@include包含在一个父规则中时,在混合器中的规则最终会生成父规则中的嵌套规则。举个例子,看看下边的Sass代码,这个例子中使用了no-bullets这个混合器:

ul.plain {
color : #444;
@include no-bullets;
}

  Sass的@include指令会将引入混合器的那行代码替换成混合器里面的内容。最终,上边的例子代码如下所示:

ul.plain {
color : #444;
list-style : none;
}
ul.plain li {
list-style-image : none;
list-style-type : none;
margin-left : 0px;
}

  混合器中的规则甚至可以用Sass的父选择器标识符 & 。使用起来跟不用混合器时一样,Sass解开嵌套规则时,用父规则中的选择器替代 & 。

  如果一个混合器只包含CSS规则,不包含属性,那么这个混合器就可以在文档的顶部调用,写在所有的CSS规则之外。

5.3 给混合器传参

  当@include混合器时,参数其实就是可以赋值给CSS属性值的变量。例如:

@mixin link-colors ($normal , $hover , $visited) {
color : $normal;
& :hover { color : $hover; }
& :visited { color : $visited; }
}

  当混合器被@include时,你可以把它当作一个CSS函数来传参。如:

a {
@include link-colors ( blue , red , green );
}

  Sass最终生成的是:

a { color : blue; }
a :hover { color : red; }
a :visited { color : green; }

  当你@include混合器时,有时候可能会很难区分每个参数是什么意思,参数之间是一个什么样的顺序。为了解决这个问题,Sass允许通过语法$name : value 的形式指定每个参数的值。这种形式的传参,参数顺序就不必再在乎了,只需要保证没有漏掉参数即可:

a {
@include link-colors {
$normal : blue,
$visited : green,
$hover : red
};
}

5.4 默认参数值

  为了在@include混合器时不必传入所有的参数,我们可以给参数指定一个默认值。参数默认值使用$name : default-value 的声明形式,默认值可以是任何有效的CSS属性值,甚至是其他参数的引用,如下代码所示:

@mixin link-colors (
$normal ,
$hover : $normal ,
$visited : $normal
) {
color : $normal ;
& :hover { color : $hover; }
& :visited { color : $visited; }
}

  如果像下面这样调用:@include link-colors(red) , $hover和$visited也会被自动赋值为red。

  混合器只是Sass样式重用特性中的一个,如果想要重用语义化的类,可以使用Sass另一个重要的重用特性:选择器继承

 6. 选择器继承

  选择器继承是说一个选择器可以继承为另一个选择器定义的所有样式这个通过@extend语法实现。例如:

.error {
border : 1px red;
background-color : #fdd;
}
.seriousError {
@extend .error;
border-width : 3px;
}

  上述代码中,.seriousError将会继承样式表中任何位置处为.error定义的所有样式。以class="seriousError"修饰的HTML元素最终的展示效果就好像是class="seriousError error"。

6.1 何时使用继承

  因为继承是基于类(有时是基于其他类型的选择器)的,所以继承应该是建立在语义化的关系上。当一个元素拥有的类表明它属于另一个类,这时使用继承再合适不过。

6.2 继承的高级用法

  最常用的一种高级用法是继承一个HTML元素的样式,但是默认的浏览器样式不会被继承。例如,下面这段代码定义了一个名为disabled的类,样式修饰使它看上去像一个灰掉的超链接。通过继承 a 这一超链接元素来实现:

.disabled {
color : gray;
@extend a;
}

  假如一条样式规则继承了一个复杂的选择器,那么它只会继承这个复杂选择器命中的元素所应用的样式。举例来说,如果 .seriousError @extend  .important .error , 那么 .important .error 和h1 .important .error 的样式都会被 .seriousError继承,但是 .important 或者 .error下的样式则不会被继承。

  如果一个选择器序列(#main .seriousError)@extend另一个选择器(.error),那么只有完全命中#main .seriousError 这个选择器的元素才会继承 .error的样式,就像单个类名继承那样。拥有class="seriousError"的 .main元素之外的元素不会受到影响。

  像#main .error这种选择器序列是不能被继承的。因为从#main .error中继承的样式一般情况下会跟直接从 .error中继承的样式基本一致。 

6.3 继承的工作细节

  关于@extend有两个要点

  ▶  跟混合器相比,继承生成的CSS代码相对更少。

  ▶  继承遵从CSS层叠的规则。

6.4 使用继承的最佳实践

  继承会在生成CSS时复制选择器而不会复制大段属性,如果不小心,可能造成生成的CSS中包含大量的选择器复制。避免这种情况的最好方法就是不要在CSS规则中使用后代选择器去继承CSS规则。