Sass 基础

Sass 基础

用着用着就会顺手的吧

Web Notes

2017.08.17

👣 #css #scss #sass

该来的总会来,用到的时候总是要去学习的。Bootstrap 4 进入 Beta,就想着是不是要折腾一下博客模板,这次就试试用 Scss 来更新吧,先上手再说。

安装 Sass

工欲善其事,必先利其器,先把 Sass 装好才能干活。Sass 依赖于 Ruby,安装 Ruby 就不多说了,之后直接:

sudo gem install sass

安装好 Sass 之后,你就可以通过 sass input.scss output.css 转换 Sass 文件了。当然也可以在终端命令中跟踪 Sass 文件或文件夹变化,方便调试:

sass --watch app/sass:public/stylesheets

Sass 文件

Sass 支持两种类型的文件:.sass,文档内不使用大括号和分号,另一种就是 .scss 文件,这种和我们平时写的 CSS 文件格式差不多,使用大括号和分号。通常建议使用后缀名为 .scss 的文件,以避免 sass 后缀名的严格格式要求报错。

使用变量

Sass 让人们受益的一个重要特性就是它为 CSS 引入了变量。你可以把反复使用的 CSS 属性值定义成变量,然后通过变量名来引用它们,而无需重复书写这一属性值。或者,对于仅使用过一 次的属性值,你可以赋予其一个易懂的变量名,让人一眼就知道这个属性值的用途。

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

变量声明

$highlight-color: #F90;
$basic-border: 1px solid black;
$plain-font: "Myriad Pro", Myriad, "Helvetica Neue", Helvetica, sans-serif;

与 CSS 属性不同,变量可以在 CSS 规则块定义之外存在。当变量定义在 CSS 规则块内,那么该变量只能在此规则块内使用。如果它们出现在任何形式的 {...} 块中(如 @media 或者 @font-face 块),情况也是如此:

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

// 编译后
nav {
  width: 100px;
  color: #F90;
}

上面这段代码中,$nav-color 这个变量定义在了规则块外边,所以在这个样式表中都可以像 nav 规则块那样引用它。$width 这个变量定义在了 nav{ } 规则块内,所以它只能在 nav 规则块内使用。这意味着是你可以在样式表的其他地方定义和使用另外的 $width 变量,不会对这里造成影响。

变量引用

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

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

// 编译后
.selected {
  border: 1px solid #F90;
}

在声明变量时,变量值也可以引用其他变量。当你通过粒度区分,为不同的值取不同名字时,这相当有用。下例在独立的颜色值粒度上定义了一个变量,且在另一个更复杂的边框值粒度上也定义了一个变量:

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

// 编译后
.selected {
  border: 1px solid #F90;
}

变量命名技巧

Sass 的变量名可以与 CSS 中的属性名和选择器名称相同,包括中划线和下划线。这完全取决于个人的喜好,有些人喜欢使用中划线来分隔变量中的多个词(如 $highlight-color),而有些人喜欢使用下划线(如 $highlight_color)。使用中划线的方式更为普遍,我也偏向于这种。

不过,Sass 并不想强迫任何人一定使用中划线或下划线,所以这两种用法相互兼容。用中划线声明的变量可以使用下划线的方式引用,反之亦然。这意味着即使 compass 选择用中划线的命名方式,这并不影响你在使用 compass 的样式中用下划线的命名方式进行引用:

$link-color: blue;
a {
  color: $link_color;
}

// 编译后
a {
  color: blue;
}

实际上,在 Sass 的大多数地方,中划线命名的内容和下划线命名的内容是互通的,除了变量,也包括对混合器和 Sass 函数的命名。但是在 Sass 中纯 CSS 部分不互通,比如类名、ID 或属性名。

嵌套 CSS 规则

CSS 中重复写选择器是非常恼人的。如果要写一大串指向页面中同一块的样式时,往往需要 一遍又一遍地写同一个 ID

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

像这种情况,Sass 可以让你只写一遍,且使样式可读性更高。在 Sass 中,你可以像俄罗斯套娃那样在规则块中嵌套规则块。Sass 在输出 CSS 时会帮你把这些嵌套规则处理好,避免你的重复书写。

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

/* 编译后 */
#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 }
}

/* 编译后 */
#content { background-color: #f5f5f5 }
#content aside { background-color: #eee }

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

父选择器的标识符 &

一般情况下,Sass 在解开一个嵌套规则时就会把父选择器(#content)通过一个空格连接到子选择器的前边(articleaside)形成(#content article#content aside)。这种在 CSS 里边被称为后代选择器,因为它选择 ID 为content 的元素内所有命中选择器 articleaside 的元素。但在有些情况下你却不会希望 Sass 使用这种后代选择器的方式生成这种连接。

最常见的一种情况是当你为链接之类的元素写:hover 这种伪类时,你并不希望以后代选择器的方式连接。比如说,下面这种情况 Sass 就无法正常工作:

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

这意味着 color: red 这条规则将会被应用到选择器 article a :hoverarticle 元素内链接的所有子元素在被 hover 时都会变成红色。这是不正确的!你想把这条规则应用到超链接自身,而后代选择器的方式无法帮你实现。

解决之道为使用一个特殊的 Sass 选择器,即父选择器。在使用嵌套规则时,父选择器能对于嵌套规则如何解开提供更好的控制。它就是一个简单的 & 符号,且可以放在任何一个选择器可出现的地方,比如 h1 放在哪,它就可以放在哪。

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

/* 编译后 */
article a { color: blue }
article a:hover { color: red }

当包含父选择器标识符 & 的嵌套规则被打开时,它不会像后代选择器那样进行拼接,而是 & 被父选择器直接替换。在为父级选择器添加 :hover 等伪类时,这种方式非常有用。同时父选择器标识符还有另外一种用法,你可以在父选择器之前添加选择器。举例来说,当用户在使用 IE 浏览器时,你会通过 JavaScript 在 <body> 标签上添加一个 ie 的类名,为这种情况编写特殊的样式如下:

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

/*编译后*/
#content aside {color: red};
body.ie #content aside { color: green }

Sass 在选择器嵌套上是非常智能的,即使是带有父选择器的情况。当 Sass 遇到群组选择器(由多个逗号分隔开的选择器形成)也能完美地处理这种嵌套。

群组选择器的嵌套

在 CSS 里边,选择器 h1h2h3 会同时命中 h1 元素、h2 元素和 h3 元素。与此类似,.buttonbutton 会命中 button 元素和类名为 .button 的元素。这种选择器称为群组选择器。群组选择器的规则会对命中群组中任何一个选择器的元素生效。

.button, button {
  margin: 0;
}

当看到上边这段代码时,你可能还没意识到会有重复性的工作。但会很快发现:如果你需要在一个特定的容器元素内对这样一个群组选择器进行修饰,情况就不同了。CSS 的写法会让你在群组选择器中的每一个选择器前都重复一遍容器元素的选择器。

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

非常幸运,Sass 的嵌套特性在这种场景下也非常有用。当 Sass 解开一个群组选择器规则内嵌的规则时,它会把每一个内嵌选择器的规则都正确地解出来:

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

对于内嵌在群组选择器内的嵌套规则,处理方式也一样:

nav, aside {
  a {color: blue}
}

/*编译后*/
nav a, aside a {color: blue}

处理这种群组选择器规则嵌套上的强大能力,正是 Sass 在减少重复敲写方面的贡献之一。尤其在当嵌套级别达到两层甚至三层以上时,与普通的 CSS 编写方式相比,只写一遍群组选择器大大减少了工作量。

有利必有弊,你需要特别注意群组选择器的规则嵌套生成的 CSS。虽然 Sass 让你的样式表看上去很小,但实际生成的 CSS 却可能非常大,这会降低页面的访问速度。

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

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

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

你可以用子组合选择器 > 选择一个元素的直接子元素。上例中,第一个选择器会选择 article 下的所有命中 section 选择器的元素。第二个选择器只会选择 article 下紧跟着的子元素中命中 section 选择器的元素。

在下例中,你可以用同层相邻组合选择器 + 选择 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 }
}

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

嵌套属性

在 Sass 中,除了 CSS 选择器,属性也可以进行嵌套。尽管编写属性涉及的重复不像编写选择器那么糟糕,但是要反复写 border-styleborder-widthborder-color 以及 border-* 等也是非常烦人的。在 Sass 中,你只需敲写一遍 border

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

嵌套属性的规则是这样的:把属性名从中划线 - 的地方断开,在根属性后边添加一个冒号 :,紧跟一个 { } 块,把子属性部分写在这个 { } 块中。

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

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

导入 Sass 文件

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

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

使用 Sass 的 @import 规则并不需要指明被导入文件的全名。你可以省略 .sass.scss 文件后缀。这样,在不修改样式表的前提下,你完全可以随意修改你或别人写的被导入的 Sass 样式文件语法,在 Sass 和 Scss 语法之间随意切换。

使用 Sass 部分文件

当通过 @import 把 Sass 样式分散到多个文件时,你通常只想生成少数几个 CSS 文件。那些专门为 @import 命令而编写的 Sass 文件,并不需要生成对应的独立 CSS 文件,这样的 Sass 文件称为局部文件。对此,Sass 有一个特殊的约定来命名这些文件:Sass 局部文件的文件名以下划线开头。这样,Sass 就不会在编译时单独编译这个文件输出 CSS,而只把这个文件用作导入。当你 @import 一个局部文件时,还可以不写文件的全名,即省略文件名开头的下划线。举例来说,你想导入 themes/_night-sky.scss 这个局部文件里的变量,你只需在样式表中写 @import "themes/night-sky";

局部文件可以被多个不同的文件引用。当一些样式需要在多个页面甚至多个项目中使用时,这非常有用。

默认变量值

有时需要在你的样式表中对导入的样式稍作修改,Sass 有一个功能刚好可以解决这个问题,即默认变量值。一般情况下,你反复声明一个变量,只有最后一处声明有效且它会覆盖前边的值。举例说明:

$link-color: blue;
$link-color: red;
a {
color: $link-color;
}

在上边的例子中,超链接的 color 会被设置为 red。这可能并不是你想要的结果,假如你写了一个可被他人通过 @import 导入的 Sass 库文件,你可能希望导入者可以定制修改 Sass 库文件中的某些值。使用 Sass 的 !default 标签可以实现这个目的。它很像 CSS 属性中 !important 标签的对立面,不同的是 !default 用于变量,含义是:如果这个变量被声明赋值了,那就用它声明的值,否则就用这个默认值。

$fancybox-width: 400px !default;
.fancybox {
	width: $fancybox-width;
}

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

嵌套导入

跟原生的 CSS 不同,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;
  }
}

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

原生的 CSS 导入

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

  • 被导入文件的名字以 .css 结尾;
  • 被导入文件的名字是一个 URL 地址(比如 https://fonts.googleapis.com/css?family=Open+Sans),由此可用谷歌字体 API 提供的相应服务;
  • 被导入文件的名字是 CSS 的 url() 值。

这就是说,你不能用 Sass 的 @import 直接导入一个原始的 CSS 文件,因为 Sass 会认为你想用 CSS 原生的 @import。但是,因为 Sass 的语法完全兼容 CSS,所以你可以把原始的 CSS 文件改名为 .scss 后缀,即可直接导入了。

静默注释

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

混合器

如果你的整个网站中有几处小小的样式类似(例如一致的颜色和字体),那么使用变量来统一处理这种情况是非常不错的选择。但是当你的样式变得越来越复杂,你需要大段大段的重用样式的代码,独立的变量就没办法应付这种情况了。你可以通过 Sass 的混合器实现大段样式的重用。

混合器使用 @mixin 标识符定义。看上去很像其他的 CSS @ 标识符,比如说 @media 或者 @font-face。这个标识符给一大段样式赋予一个名字,这样你就可以轻易地通过引用这个名字重用这段样式。下边的这段 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;
}

何时使用混合器

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

判断一组属性是否应该组合成一个混合器,一条经验法则就是你能否为这个混合器想出一个好的名字。如果你能找到一个很好的短名字来描述这些属性修饰的样式,比如 rounded-cornersfancy-font 或者 no-bullets,那么往往能够构造一个合适的混合器。如果你找不到,这时候构造一个混合器可能并不合适。

混合器在某些方面跟 CSS 类很像。都是让你给一大段样式命名,所以在选择使用哪个的时候可能会产生疑惑。最重要的区别就是类名是在 html 文件中应用的,而混合器是在样式表中应用的。这就意味着类名具有语义化含义,而不仅仅是一种展示性的描述:用来描述 html 元素的含义而不是 html 元素的外观。而另一方面,混合器是展示性的描述,用来描述一条 CSS 规则应用之后会产生怎样的效果。

在之前的例子中,.notice 是一个有语义的类名。如果一个 html 元素有一个 notice 的类名,就表明了这个 html 元素的用途:向用户展示提醒信息。rounded-corners 混合器是展示性的,它描述了包含它的 CSS 规则最终的视觉样式,尤其是边框角的视觉样式。混合器和类配合使用写出整洁的 html 和 CSS,因为使用语义化的类名亦可以帮你避免重复使用混合器。为了保持你的 html 和 CSS 的易读性和可维护性,在写样式的过程中一定要铭记二者的区别。

混合器中的 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;
}

/*编译后*/
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 规则之外。如果你只是为自己写一些混合器,这并没有什么大的用途,但是当你使用一个类似于 Compass 的库时,你会发现,这是提供样式的好方法,原因在于你可以选择是否使用这些样式。

给混合器传参

混合器并不一定总得生成相同的样式。可以通过在 @include 混合器时给混合器传参,来定制混合器生成的精确样式。当 @include 混合器时,参数其实就是可以赋值给 CSS 属性值的变量。如果你写过 JavaScript,这种方式跟 JavaScript 的 function 很像:

@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
  );
}

默认参数值

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

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

使用选择器继承来精简 CSS

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

//通过选择器继承继承样式
.error {
  border: 1px red;
  background-color: #fdd;
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

.seriousError不仅会继承 .error 自身的所有样式,任何跟 .error 有关的组合选择器样式也会被 .seriousError 以组合选择器的形式继承,如下代码:

//.seriousError 从 .error 继承样式
.error a{  //应用到 .seriousError a
  color: red;
  font-weight: 100;
}
h1.error { //应用到 hl.seriousError
  font-size: 1.2rem;
}

何时使用继承

想象一下你正在编写一个页面,给 html 元素添加类名,你发现你的某个类(比如说 .seriousError)另一个类(比如说 .error)的细化。你会怎么做?

  • 你可以为这两个类分别写相同的样式,但是如果有大量的重复怎么办?使用 Sass 时,我们提倡的就是不要做重复的工作。
  • 你可以使用一个选择器组(比如说 .error.seriousError)给这两个选择器写相同的样式。如果 .error 的所有样式都在同一个地方,这种做法很好,但是如果是分散在样式表的不同地方呢?再这样做就困难多了。
  • 你可以使用一个混合器为这两个类提供相同的样式,但当 .error 的样式修饰遍布样式表中各处时,这种做法面临着跟使用选择器组一样的问题。这两个类也不是恰好有相同的样式。你应该更清晰地表达这种关系。
  • 综上所述你应该使用 @extend。让 .seriousError.error 继承样式,使两者之间的关系非常清晰。更重要的是无论你在样式表的哪里使用 .error.seriousError 都会继承其中的样式。

继承的高级用法

任何 CSS 规则都可以继承其他规则,几乎任何 CSS 规则也都可以被继承。大多数情况你可能只想对类使用继承,但是有些场合你可能想做得更多。最常用的一种高级用法是继承一个 html 元素的样式。尽管默认的浏览器样式不会被继承,因为它们不属于样式表中的样式,但是你对 html 元素添加的所有样式都会被继承。

接下来的这段代码定义了一个名为 disabled 的类,样式修饰使它看上去像一个灰掉的超链接。通过继承 a 这一超链接元素来实现:

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

假如一条样式规则继承了一个复杂的选择器,那么它只会继承这个复杂选择器命中的元素所应用的样式。举例来说, 如果 .seriousError @extend .important.error,那么 .important.errorh1.important.error 的样式都会被 .seriousError 继承,但是 .important 或者 .error 的样式则不会被继承。这种情况下你很可能希望 .seriousError 能够分别继承 .important 或者 .error 下的样式。

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

#main .error 这种选择器序列是不能被继承的。这是因为从 #main .error 中继承的样式一般情况下会跟直接从 .error 中继承的样式基本一致,细微的区别往往使人迷惑。

继承的工作细节

  • 跟混合器相比,继承生成的 CSS 代码相对更少。因为继承仅仅是重复选择器,而不会重复属性,所以使用继承往往比混合器生成的 CSS 体积更小。如果你非常关心你站点的速度,请牢记这一点。
  • 继承遵从 CSS 层叠的规则。当两个不同的 CSS 规则应用到同一个html元素上时,并且这两个不同的 CSS 规则对同一属性的修饰存在不同的值,CSS 层叠规则会决定应用哪个样式。相当直观:通常权重更高的选择器胜出,如果权重相同,定义在后边的规则胜出。

使用继承的最佳实践

通常使用继承会让你的 CSS 美观、整洁。因为继承只会在生成 CSS 时复制选择器,而不会复制大段的 CSS 属性。但是如果你不小心,可能会让生成的 CSS 中包含大量的选择器复制。

避免这种情况出现的最好方法就是不要在 CSS 规则中使用后代选择器(比如 .foo .bar)去继承 CSS 规则。如果你这么做,同时被继承的 CSS 规则有通过后代选择器修饰的样式,生成 CSS 中的选择器的数量很快就会失控:

.foo .bar { @extend .baz; }
.bip .baz { a: b; }

在上边的例子中,Sass 必须保证应用到 .baz 的样式同时也要应用到 .foo .bar(位于 class="foo" 的元素内的 class="bar" 的元素)。例子中有一条应用到 .bip .baz(位于 class="bip" 的元素内的 class="baz" 的元素)的 CSS 规则。当这条规则应用到 .foo .bar 时,可能存在三种情况,如下代码:

<!-- 继承可能迅速变复杂 -->
<!-- Case 1 -->
<div class="foo">
  <div class="bip">
    <div class="bar">...</div>
  </div>
</div>

<!-- Case 2 -->
<div class="bip">
  <div class="foo">
    <div class="bar">...</div>
  </div>
</div>

<!-- Case 3 -->
<div class="foo bip">
  <div class="bar">...</div>
</div>

为了应付这些情况,Sass 必须生成三种选择器组合(仅仅是 .bip .foo .bar 不能覆盖所有情况)。如果任何一条规则里边的后代选择器再长一点,Sass 需要考虑的情况就会更多。实际上 Sass 并不总是会生成所有可能的选择器组合,即使是这样,选择器的个数依然可能会变得相当大,所以如果允许,尽可能避免这种用法。

Ads by Google

林宏

Frank Lin

Hey, there! This is Frank Lin (@flinhong), one of the 1.41 billion . This 'inDev. Journal' site holds the exploration of my quirky thoughts and random adventures through life. Hope you enjoy reading and perusing my posts.

YOU MAY ALSO LIKE

CSS 选择器笔记

Web Notes

2016.12.01

CSS 选择器笔记

CSS 选择器用于选择需要添加样式的元素,其重要性不言而喻。最近更新博客模板就不断调整 CSS 样式,好多时候就因为选择器不熟悉而不能应用样式,所以干脆好好学习总结一下常用的 CSS 选择器,也为下一步学习 jQuery 做好准备。

Syntax highlight with Rouge in Jekyll

Web Notes

2017.03.18

Syntax highlight with Rouge in Jekyll

By default, Jekyll 3 and versions above integrated with Rouge, a pure Ruby syntax highlighter which supports over 100 languages. Since Rouge themes are compatible with Pygments's stylesheets, it’s nice for us to choose a favourable style.