在SassConf大会上,给我们传递了Sass3.3的新特性。这些新特性有很多意义,特别是@at-root
指令,这让你的代码会得更佳清洁。今天我们主要一起来了解Sass中的@at-root
特性的使用规范。
在具体了解@at-root
规范之间,我们先来回忆Sass的嵌套功能。简单的来看一个示例。在我们的CSS中常见有这样的一段代码:
.foo {
color:green;
}
.foo .bar {
color:gray;
}
回到Sass中,实现上面的样式,我们可以使用Sass的嵌套来完成:
.foo {
color: green;
.bar {
color: gray;
}
}
除了上面的方法之外,还可以通过连体符&
来实现:
.foo {
color: green;
& .bar {
color: gray;
}
}
如若简单实现:
.foo .bar {
color: gray;
}
还可以使用另外一种特殊方式来实现:
.bar {
.foo & {
color: gray;
}
}
在我们CSS中有一种命名方式是BEM,如:
.block {}
.block__element{}
.block--modifier{}
试想我们在Sass中是否可以通过下面的方式来实现上面样式代码:
#{&}_element{}
不仿我们来验证一下:
.block {
color: red;
#{&}__element {
color:blue;
}
#{&}--modifier {
color: orange;
}
}
悲催的是,编译出来的CSS并不是我们想要的代码:
.block {
color: red; }
.block .block__element {
color: blue; }
.block .block--modifier {
color: orange; }
但在LESS和Stylus中,能很好的实现BEM类名的形式。此时在想,在Sass中有没有这样的功能呢?值得幸运的是,在Sass3.3中新增加了@at-root
特性,能实现上面BEM的特性:
.block {
color: red;
@at-root #{&}__element {
color: blue;
}
@at-root #{&}--modifier {
color:orange;
}
}
@at-root
特性
前而的例子告诉我们@at-root
是什么。通过他可以告诉Sass,你不想嵌套选择器。当使用&
选择器时,就算你不想嵌套选择器,Sass也会自动嵌套。但往往很多时候,我们是不想要嵌套选择器,例如BEM。使用@at-root
和#{&}
可以引用父(在Sass中总是引用父选择器)和插值,可以嵌套,做一些其他的事情。接下来,我们通过一些实例来说明@at-root
的特性。
@at-root
运行环境
@at-root
是Sass的新特性。要想能正常的运行@at-root
,首先需要先安装Sass的全新版本:只要确保你的电脑上已经安装了Ruby,你可以直接打开终端命令运行下面的命令安装Sass:
$ gem install sass --pre
如果你无法确认,你以前安装的Sass是不是最新版本,你可以通过:
$ sass -v
显示的版本信息:
Sass 3.3.0.rc.2 (Maptastic Maple)
如果不是,你可以使用下面的命令更新Sass:
$ gem update sass
注意,如果你的系统是刚升级到OS X 10.9 Mavericks,你得重新更新Command Line Tools:
$ xcode-select --install
重启您的终端命令,重新执行上面的命令就可以获取最新版本的Sass。请注意,这是一个未发表的版本,因为它还在开发中,但现在玩一点都不受影响,还可以体验Sass中新增加的一些特性。
Ben Frain在The things I want from Sass aren’t the things I thought I wanted一文中也详细的描述了Sass中新增的特性,如:Source Maps。
@at-root
规范
@at-root
使用规范是如何工作,这里我们通过一些测试用例来做说明:
内联选择器模式
SCSS
.foo {
@at-root .bar {
color:gray;
}
}
CSS
.bar {
color: gray;
}
测试用例可以说明,@at-root
的内联选择器模式,将不会让你的选择器发生任何的嵌套,直接移除了父选择。在来看一个嵌套深一点的用例:
SCSS
.foo {
@at-root .bar {
color: gray;
@at-root button{
color:red;
@at-root span {
color: orange;
}
}
}
}
CSS
.bar {
color: gray;
}
button {
color: red;
}
span {
color: orange;
}
在SCSS中嵌套,使用@at-root
内联选择器模式,编译出来的CSS无任何嵌套,让代码更加的简单。回到SCSS中的嵌套中,如果不使用@at-root
内联选择器模式,将会按代码的层级关系一层一层往下嵌套。如上例,将用例中的@at-root
去掉之后,将会编译出像下面的CSS代码(了解过Sass)的同学,一点都不会觉得奇怪。
.foo .bar {
color: gray;
}
.foo .bar button {
color: red;
}
.foo .bar button span {
color: orange;
}
@at-root
和&
的结合
&
在Sass中所起的作用,文章开头就简单的进行演示了。在@at-root
中也同样可以配合&
一起使用,下面我们同样来看几个用例:
SCSS
.foo {
@at-root .bar & {
color:gray;
}
}
CSS
.bar .foo {
color: gray;
}
大家很容易发现,这个示例和不加@at-root
的SCSS代码一样,可以编译出完全相同的代码:
.foo {
.bar & {
color:gray;
}
}
同样的,&
符和@at-root
按下面的方式一起工作:
SCSS
.foo {
@at-root & .bar {
color:gray;
}
}
.foo {
@at-root &.bar {
color:gray;
}
}
CSS
.foo .bar {
color: gray;
}
.foo.bar {
color: gray;
}
同样如此,这种方式与不加@at-root
方式,运行的效果是一样的:
.foo {
& .bar {
color:gray;
}
}
.foo {
&.bar {
color:gray;
}
}
如此说明,在Sass中同时使用@at-root
和&
起到的作用是一样的,换句话说,这样并没有带来新的特性,而且在整个开发中还带来了额外的工作量。
@at-root
和#{&}
结合
Sass有脚本模式#{}
,他和&
不同之处是,&
只用作选择器,它只能出现在一个复合的开始选择器,类似于一个类型选择器,如a
或者h1
。但#{}
他表示的是一个插值,它可以用在任何地方。同样的,当@at-root
和#{&}
一起使用时,可以给我们的开发带来极大的方便与优势。例如:
SCSS
.foo {
@at-root .bar #{&} {
color: gray;
}
}
.foo {
@at-root #{&} .bar {
color:gray;
}
}
.foo {
@at-root #{&}.bar {
color:gray;
}
}
CSS
.bar .foo {
color: gray;
}
.foo .bar {
color: gray;
}
.foo.bar {
color: gray;
}
可能会让你感到意外与失望,因为你从上面的代码之中并没有看到他有特别之处。或者说能帮你减少什么?也不能帮你优化什么?不过不用太心急,接着往下看。
先快速回忆一下BEM,如:
.block {} //Block
.block__element{} //Element
.block--modifier{} //Modifier
此时使用@at-root
就能尽显其英雄本色:
SCSS
.block {
color:red;
@at-root #{&}__element{
color:green;
}
@at-root #{&}--modifier {
color:blue;
}
}
CSS
.block {
color: red;
}
.block__element {
color: green;
}
.block--modifier {
color: blue;
}
到了这里,你是否体会到@at-root
在BEM中是具有多大的优势了吧。不急,后面将来个实例,让你一览其整个过程。
@at-root
区块处理方式
@at-root
还具有区块处理的方式,可以将其内容以下面的方式进行嵌套:
.block {
@at-root {
#{&}__element{...}
#{&}--modifier{...}
}
}
但在具体的使用过程中,有一些细节需要处理。先来看一个简单的示例:
SCSS
.foo {
@at-root{
.bar #{&} {
color:gray;
}
}
}
CSS
.bar .foo {
color: gray;
}
从运行效果上可以看出,和不按区块显示效果是一致的。
在@at-root
的块嵌套中,只会影响最近的子选择器,如:
SCSS
.foo {
@at-root {
.bar {
.baz {
color: gray;
} //.bar .baz {color:gray;}
@at-root {
.baz {
.bac {
color: green;
}// .baz .bac {color:green;}
}
}
}
@at-root {
.baz {
.bac {
color: red;
}//.baz .bac {color:red;}
}
}
}
}
.foo {
@at-root {
.bar #{&}{
.baz {
color: gray;
} //.bar .foo .baz {color:gray;}
@at-root {
.baz #{&}{
.bac {
color: green;
}//.bar .bar .foo .bac {color:green;}
}
}
}
@at-root {
.baz #{&}{
.bac {
color: red;
}//.baz .foo .bac{color:red;}
}
}
}
}
CSS
.bar .baz {
color: gray;
}
.baz .bac {
color: green;
}
.baz .bac {
color: red;
}
.bar .foo .baz {
color: gray;
}
.baz .bar .foo .bac {
color: green;
}
.baz .foo .bac {
color: red;
}
这里有一个细节要特别注意,在使用@at-root
块时,引用#{&}
会略有不同。
@at-root
和with
或without
在@at-root
中无法自动移除默认的指令,如@media
或者@supports
,先来看一个示例:
SCSS
@media print {
@supports (transorm-origin: 5% 5%) {
.foo {
@at-root {
.bar #{&} {
color: gray;
}
}
}
}
}
CSS
@media print {
@supports (transorm-origin: 5% 5%) {
.bar .foo {
color: gray;
}
}
}
默认情况之下,@at-root
只是通过#{&}
来排除选择器。然而,它也可以使用@at-root
和whit
或者without
变得更加的灵活。比如,您可以使用@at-root
和without
在媒体查询的时候实现另一种样式。例如:
SCSS
@media print {
.page {
width: 8in;
@at-root (without: media) {
width: 960px;
}
}
}
CSS
@media print {
.page {
width: 8in;
}
}
.page {
width: 960px;
}
你可以使用@at-root (without: ...)
移除外面的任何指令。还可以通过空格用于移除多个指令,例如@at-root (without: media supports )
同时移除了@media
和@supports
外部指令。
SCSS
@media print {
@supports ( transform-origin: 5% 5% ) {
.foo {
color: green;
@at-root (without: media supports){
color: gray;
}
}
}
}
CSS
@media print {
@supports (transform-origin: 5% 5%) {
.foo {
color: green;
}
}
}
.foo {
color: gray;
}
特别声明:到写本文的时候,运行@at-root (with:...)
未成功过。如果有使用成功的同学,希望能分享使用案例。
案例:@at-rooot
在BEM中的应用
什么是BEM就不在多说了,来看一个简单的应用示例,假如你的应用中有一段这样的代码:
.speech-bubble{}
.speech-bubble__header{}
.speech-bubble__text{}
.speech-bubble__text--link{}
前面介绍过,@at-root
特性允许在Sass中嵌套,编译出来的代码不会发生嵌套。这可以很好的组织你的代码。为了更好的说明,我们先回到Sass中的嵌套:
SCSS
.speech-bubble {
color: purple;
.speech-bubble__header {
color: orange;
}
.speech-bubble__text {
color: black;
.speech-bubble__text--link {
color:green;
}
}
}
CSS
.speech-bubble {
color: purple;
}
.speech-bubble .speech-bubble__header {
color: orange;
}
.speech-bubble .speech-bubble__text {
color: black;
}
.speech-bubble .speech-bubble__text .speech-bubble__text--link {
color: green;
}
这样编译出来的CSS离开了使用BEM的宗旨,也就失去了存在的意义。这个时候或许想到了Sass的#{&}
,想通过他来解决:
SCSS
.speech-bubble {
color: purple;
#{&}__header {
color: orange;
}
#{&}__text {
color: black;
#{&}--link {
color:green;
}
}
}
CSS
.speech-bubble {
color: purple;
}
.speech-bubble .speech-bubble__header {
color: orange;
}
.speech-bubble .speech-bubble__text {
color: black;
}
.speech-bubble .speech-bubble__text .speech-bubble .speech-bubble__text--link {
color: green;
}
编译出来的CSS说明一切,特别是用在.speech-bubble__text--link
元素上的样式:
.speech-bubble .speech-bubble__text .speech-bubble .speech-bubble__text--link {
color: green;
}
可谓是差之毫厘失之千里。既然如此,我们一起来看@at-root
能帮我们做什么:
SCSS
.speech-bubble {
color: purple;
@at-root #{&}__header {
color: orange;
}
@at-root #{&}__text {
color: black;
@at-root #{&}--link {
color:green;
}
}
}
CSS
.speech-bubble {
color: purple;
}
.speech-bubble__header {
color: orange;
}
.speech-bubble__text {
color: black;
}
.speech-bubble__text--link {
color: green;
}
好极了!完美实现BEM与Sass的结合。
不过大家先别急着高兴,Scott Kellum在评论中提出了一个要点。他通过Sass的Mixin给@at-root
与BEM的结合创建了一个更完美的语法:
Scott的Mixins:
//elements get appended with "__" and the $name
@mixin e($name) {
@at-root #{&}__#{$name} {
@content;
}
}
//modifiers get appended with "--" and the $name
@mixin m($name) {
@at-root #{&}--#{$name} {
@content;
}
}
SCSS
.speech-bubble {
color: purple;
@include e(header) {
color:orange;
}
@include e(text) {
color:black;
@include m(link){
color:green;
}
}
}
CSS
.speech-bubble {
color: purple;
}
.speech-bubble__header {
color: orange;
}
.speech-bubble__text {
color: black;
}
.speech-bubble__text--link {
color: green;
}
是不是很完美,而且通过Mixin是不是更方便。如果你还在想他是否具有通用性。我们不仿在使用大家最常见的“媒体对象”来验证一回。
在媒体对象中存在两种情形,一种图片居左显示,另一种图片居右显示,同时我们给他的主体创建一个BFC。常见的结构如下:
HTML
<div class="media">
<img src="logo.png" alt="Foo Corp logo" class="media__img--rev">
<div class="media__body">
<h3 class="alpha">Welcome to Foo Corp</h3>
<p class="lede">Foo Corp is the best, seriously!</p>
</div>
</div>
SCSS
.media {
@extend %clearfix;
background-color: #f5f5f5;
@include e(img) {
float: left;
display: inline;
margin-right: 10px;
@include m(rev){
float: right;
display: inline;
margin-left: 10px;
}
}
@include e(body){
overflow: hidden;
}
}
注:代码中引用了一个%clearfix
用来实现清除浮动,具体制作方法,请点击W3cplusSass核心库中的mixins。
CSS
.media {
*zoom: 1;
}
.media:before, .media:after {
content: "";
display: table;
}
.media:after {
clear: both;
overflow: hidden;
}
.media {
background-color: #f5f5f5;
}
.media__img {
float: left;
display: inline;
margin-right: 10px;
}
.media__img--rev {
float: right;
display: inline;
margin-left: 10px;
}
.media__body {
overflow: hidden;
}
完美终结!
参考资料
- Sass 3.3 @at-root & BEM!
- Writing modular CSS (BEM/OOCSS) selectors with Sass 3.3
- Sass Changelog
- Spec for the @at-root directive
- Interpolation with parent reference (#{&}) inside multiple selector generates incorrect selector
- The things I want from Sass aren’t the things I thought I wanted
- BEM
总结
@at-root
是Sass3.3版本中新增的特性,他可以让你在Sass中嵌套选择器,或者做别的事情,但编译出来的CSS却不会发生嵌套。让你的文件更简洁,更干净,更易于维护。特别是@at-root
特性与CSS的BEM结合在一起,让你在编写样式时不用在恐惧BEM的长选择器的方式,同时让你的BEM更显风采。
当然@at-root
是一个新特性,他有很多功能也还在完善当中,比如说@at-rooot (with:)
,@at-root (without: rule)
和@at-root (without: all)
等。如果您对@at-root
特性有更好的体验与理解,欢迎能在下面的评论中留下您的经验。
如需转载,烦请注明出处:http://www.w3cplus.com/preprocessor/Sass-3-3-new-feature-at-root-bem.html