Scss 初识 @extend 与 @mixin

前言

前面我们已经学习了 Scss 的基础。其实,能够运用之前的技巧已经足够了,在日常开发中,对 CSS 的熟练使用比对 Scss 的熟练使用更重要。但本文集既然是讲学习 Scss,自然是希望能够对 Scss 有更深的研究。当然,不实用、复杂且不易懂的内容是被剔除的,毕竟学习工具的首要目的还是实用。

@extend实现样式复用

想必大家在日常的开发中常常为这种情况苦恼:例如我在写一个页面,页面里好几个地方是相同的样式,又或者这个地方的样式有一部分与另一个地方的样式完全重合,这里只是有一点不同而已,但是使用 CSS 并不能实现代码的复用,只能用 CV 大法搬运代码。为了解决这样的需求和场景,Scss 便实现了该功能,使用@extend便可以实现对重复代码的复用。

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

这里将.error的样式在seriousError里进行复用,代码看起来简洁美观。当然,@extend不仅仅支持类选择器,也支持伪类选择器。

.hoverlink {
  @extend a:hover;
}
a:hover {
  text-decoration: underline;
}

上一篇文章我们提到,Scss 无论再怎么书写,最终都会被编译为 CSS。@extend方法的本质其实就是将.hoverlink添加到a:hover中。不是代码中表现的那样,将其他类名下的样式“拷贝”到当前类名下面,而是将当前类名添加到其他被 extend 的类名里,成为“组选择器”。上述代码编译为:

a:hover, .hoverlink {
  text-decoration: underline; }

但不论如何,这样的书写让动辄几百、几千行的 CSS 代码清晰了很多。既然是写得更少,@extend自然是不止使用一次,在一个样式里,可以@extend多个其他类名下的样式。

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.attention {
  font-size: 3em;
  background-color: #ff0;
}
.seriousError {
  @extend .error;
  @extend .attention;
  border-width: 3px;
}

仅仅如此吗?如果只是如此,那@extend的能力依然不算强大。既然是复用,那就贯彻到底。我们可以在一个类名下@extend另一个类名的样式,那这个类名可以在其他类名下被@extend吗?答案是肯定的:

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.seriousError {
  @extend .error;
  border-width: 3px;
}
.criticalError {
  @extend .seriousError;
  position: fixed;
  top: 10%;
  bottom: 10%;
  left: 10%;
  right: 10%;
}

怎么样?是不是非常强大?这可以让平时反复出现的样式代码变得简洁多了。但代码量大的情况下依然是需要写注释的,类名庞杂的时候,需要注释@extend的样式在第几行,这对于你返回修改样式时非常有用。

@mixin实现更强大的复用

@mixin其实可以理解为函数(Scss有函数,但本文集不打算涉及,且Scss的函数并没有多易用)。@mixin可以理解为声明语句,例如,声明一个混入:

@mixin large-text {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: #ff0000;
}

large-text 并不是一个类名,仅仅是一个“混入”名而已,也可以说是“代码块变量”。让我们回顾下前面讲到的知识,是时候串联起来了。& 符在@mixin里也是可用的:

@mixin clearfix {
  display: inline-block;
  &:after {
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
  }
  * html & { height: 1px }
}

“变量”声明了,哪里使用呢?如何使用呢?其实,在标题里我们就说了,@mixin只是更强大的复用而已,后面我们会讲到其与@extend的区别。前面讲到,@mixin可以理解为函数,函数声明了,是不是需要调用呢?要想使用@mixin,可以在需要复用样式的类名下使用@include进行引用。不在一个选择器下使用这些样式,光使用@include是会无效的!

.page-title {
  @include large-text;
  padding: 4px;
  margin-top: 10px;
}

@extend一样,@mixin内部也可以@include其他@mixin的样式:

@mixin compound {
  @include highlighted-background;
  @include header-text;
}

讲到这里,@mixin依然与@extend一样,并没有突显什么强大的功能。仅仅是样式的复用而已,而且比@extend还麻烦。接下来,来看看@mixin与众不同的地方:参数。

@mixin sexy-border($color, $width) {
  border: {
    color: $color;
    width: $width;
    style: dashed;
  }
}
p { @include sexy-border(blue, 1in); }

如同函数一样,@mixin可以携带参数,在@include使用时动态传入,依据不同的需求做出不同的选择。因为是参数,其实就是变量,所以必须使用$开头。与 ES6 中的函数一样,@mixin的参数也支持默认值:

@mixin sexy-border($color: blue, $width: 1in) {
  border: {
    color: $color;
    width: $width;
    style: dashed;
  }
}

不仅如此,@mixin还支持类似于 Vue 插槽一般的功能:

@mixin apply-to-ie6-only {
  * html {
    @content;
  }
}
@include apply-to-ie6-only {
  #logo {
    background-image: url(/logo.gif);
  }
}

上述代码中,@content起到一个占位符的作用,与 Vue 中 slot 作为预留空间的作用一样,在@include时可以添加各种样式,这些样式会被添加到@content的位置,最终编译为 CSS 时会被统一编译到一起。但这里最重要的在于,替换@content的代码的作用域在原来的位置,@content相当于一个指向该位置的指针而已。也就是说,被传进去代替@content的代码片段不能使用在@mixin里定义的变量!

* html #logo {
  background-image: url(/logo.gif);
}

为了使@mixin@include更加方便,Scss 增加了这两者的语法糖,@mixin可以简写为 =@include可以简写为+,并且{}可以省略:

=apply-to-ie6-only
  * html
    @content

+apply-to-ie6-only
  #logo
    background-image: url(/logo.gif)

@mixin 与 @extend 的区别与选择

之前我们讲过,@extend的本质其实是将一个样式的类名组合在一起形成组选择器,共用一段样式代码,而@mixin则恰恰相反。

@extend

.button {  
    background: green;  
}
.button-1 {  
    @extend .button;  
}
.button-2 {  
    @extend .button;  
}

// 编译后
.button, 
.button-1, 
.button-2 {  
    background: green;  
}

@mixin

@mixin button {  
    background-color: green;  
}
.button-1 {  
    @include button;  
} 
.button-2 {  
    @include button;  
}

// 编译后
.button {  
    background-color: green;  
}
.button-1 {  
    background-color: green;  
}
.button-2 {  
    background-color: green;  
}

所以,二者的区别显而易见,使用@extend可以减少编译后代码量,不过这不重要,编译后的 CSS 谁会在乎呢?在一些静态的复用上,@extend更加简单方便,但@mixin的优势在于可以传参,使用上更加灵活。

但从实际来看,@extend更适合局部的复用,在复用父级样式或者兄弟样式时很有用,方便易得。使用@mixin可以像 JS 模块化一样在代码头部就初始化一些样式“混入”,在样式书写过程中如果遇到可以复用的样式(特别是需要灵活传参的样式),便可以在样式表头部声明,在任意位置复用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容