前言
前面我们已经学习了 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 模块化一样在代码头部就初始化一些样式“混入”,在样式书写过程中如果遇到可以复用的样式(特别是需要灵活传参的样式),便可以在样式表头部声明,在任意位置复用。