关于Sass实践尝试
最近项目引入Sass,并且对部分css进行了改进优化。Sass的语法学习使用是相对容易,但是结合实战,不同用法要适用于不同的场景,达到提高效率的目的才是关键。这篇文章对Sass使用技巧做一份小结,但不会一一列举Sass特性,更多知识详见官方文档。
嵌套中使用&
日常中,我们习惯创建一个盒子的时候,给容器命名一个class类,再给容器内各部分基于容器class类进行扩展命名,如:
.card {}
.card_title {}
.card_content{}
.card_extra{}
复制代码
我们总是不得不对容器class类名进行重写,而Sass可以很好地让我们少敲一些不必要的代码。这篇文章介绍了我们如何利用Sass来解决BEM命名方法产生很冗余的长命名问题,例如:
// scss
.card {
&_title {}
&_content {}
&_extra {}
}
// 编译后 css
.card_title {}
.card_content{}
.card_extra{}
复制代码
循环
不得不说sass循环着实为我们减少代码的重复编写,解放我们的双手。
@for
用法
// start <= $var < end
@for $var from start to end
// start <= $var <= end
@for $var from start through end
复制代码
若我们使用html写一个loading样式
我们的loading元素是这样的
<div class="loading">
{Array.from({ length: 12 }).map((_, i) => (
<span key={i} />
))}
</div>
复制代码
每个span
元素都是一个长方条,我们让他们旋转不同的角度,那么可以:
.loading span {
// 省略...
width: 3px;
height: 7px;
animation: roate 0.96s infinite;
@for $i from 1 through 11 {
&:nth-child(#{$i + 1}) {
animation-delay: 0.08s * $i;
transform: rotate(30deg * $i);
}
}
}
复制代码
@each
@each
可以用来循环list
或者map
数据
假设我们有四个状态样式success
、waring
、info
、error
,我们可以这样做:
// scss
$statusMap: (
success: #3ccf3c,
warning: #30baff,
info: #ff9830,
error: #ff2a2a
);
@each $header, $attrs in $statusMap {
.#{$header} {
color: $attrs;
}
}
// 编译后 css
.success {
color: #3ccf3c;
}
.warning {
color: #30baff;
}
.info {
color: #ff9830;
}
.error {
color: #ff2a2a;
}
复制代码
这样便于我们维护和添加新的状态样式。
有时我们的状态样式并不是只有特定color,还有其他属性,那么map数据可以改成
$statusMap: (
success: (
border: 1px solid rgba(60, 207, 60, 0.3),
background: rgba(60, 207, 60, 0.1),
color: #3ccf3c
),
warning: (
border: 1px solid rgba(48, 186, 255, 0.3),
background: rgba(48, 186, 255, 0.1),
color: #30baff
),
info: (
border: 1px solid rgba(255, 152, 48, 0.3),
background: rgba(255, 152, 48, 0.1),
color: #ff9830
),
error: (
border: 1px solid rgba(255, 42, 42, 0.3),
background: rgba(255, 42, 42, 0.1),
color: #ff2a2a
)
);
复制代码
然后我们可以进行双层遍历
@each $header, $attrs in $statusMap {
.#{$header} {
@each $key, $attr in $attrs {
#{$key}: $attr;
}
}
}
复制代码
编译得到我们想要的结果
.success {
border: 1px solid rgba(60, 207, 60, 0.3);
background: rgba(60, 207, 60, 0.1);
color: #3ccf3c;
}
// 省略...
复制代码
同时状态样式可以结合@mixin
使用。比如,有时候页面有些地方我们想只有color,有些又要color和background……
那么我们可以封装成@mixin
,然后传入参数决定要哪些属性值
@mixin status($options...) { // 将多个参数处理为list
// 默认color
$array: if(list.length($options) > 0, $options, color);
@each $label, $attrs in $statusMap {
.#{$label} {
@each $key in $array {
#{$key}: map.get($attrs, $key);
}
// 内容占位符
@content;
}
}
}
复制代码
现在调用这个mixin
.box1 {
@include status;
}
.box2 {
@include status(color, border) {
border-radius: 4px;
};
}
复制代码
编译后css
.box1 .success {
color: #3ccf3c;
}
.box1 .warning {
color: #30baff;
}
// 省略...
.box2 .success {
color: #3ccf3c;
border: 1px solid rgba(60, 207, 60, 0.3);
border-radius: 4px;
}
// 省略...
复制代码
At-Rules
@mixin
@mixin
是我们抽取公用样式块的利器。
用法
@mixin clearfix() {
&::after {
display: block;
clear: both;
content: "";
}
}
.box {
@include clearfix;
}
复制代码
我们还可以为@mixin
添加参数,也可以导入内容@content
。这两个@mixin
的特性我们会经常用得到。
例如我们抽取了boostrap
的表单部分片段
@mixin form-validation-state-selector($state) {
@if ($state == "valid" or $state == "invalid") {
.was-validated #{if(&, "&", "")}:#{$state},
#{if(&, "&", "")}.is-#{$state} {
@content;
}
} @else {
#{if(&, "&", "")}.is-#{$state} {
@content;
}
}
}
复制代码
这个mixin
可以给不同表单校验状态给特定的类名添加自定义的内容。
我们举例给valid
状态input
框的class类添加这个mixin
:
.form-check-input {
@include form-validation-state-selector(valid) {
$color: #198754;
border-color: $color;
&:checked {
background-color: $color;
}
}
}
复制代码
编译后css
.was-validated .form-check-input:valid, .form-check-input.is-valid {
border-color: #198754;
}
.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked {
background-color: #198754;
}
复制代码
同时,我们注意到上面的mixin
有if(&, "&", "")
。为何做这个判断呢?是因为如果定义的@mixin
里有class类,那么@include
不一定非要在某一个类嵌套里使用。例如像boostrap
直接全局使用,而不需要嵌套里面使用。
$state: valid;
@include form-validation-state-selector($state) {
~ .#{$state}-feedback,
~ .#{$state}-tooltip {
display: block;
}
}
复制代码
这时的&父选择器是为空的,因为有if(&, "&", "")
判断,那么编译的时候用空字符串代替
// 编译后 css
.was-validated :valid ~ .valid-feedback,
.was-validated :valid ~ .valid-tooltip,
.is-valid ~ .valid-feedback,
.is-valid ~ .valid-tooltip {
display: block;
}
复制代码
关于mixin
,还有我们比较少用的就是可以为@content
添加参数。
我们可以使用@content($var)
,并且当用到@include
时可以用using($type)
来接受参数。
// scss
@mixin media($types...) {
@each $type in $types {
@media #{$type} {
@content($type);
}
}
}
@include media(screen, print) using ($type) {
h1 {
font-size: 40px;
@if $type == print {
font-family: Calluna;
}
}
}
// 编译后 css
@media screen {
h1 {
font-size: 40px;
}
}
@media print {
h1 {
font-size: 40px;
font-family: Calluna;
}
}
复制代码
@extend
关于项目是否需要使用@extend
,每个人的观点都不一样。从这篇文章分析来讲,@mixin
比@extend
性能更好,能使用@mixin
则使用它。
如果确切要使用@extend
,我们必须先搞清楚选择器的层级关系,否则很容易适得其反。如果非要实际中找出比较普遍使用@extend
场景,那就是@extend
与占位符选择器 %foo
结合使用。
// scss
%heading {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: #222;
}
h1 {
@extend %heading;
font-size: 24px;
}
h2 {
@extend %heading;
font-size: 22px;
}
h3 {
@extend %heading;
font-size: 20px;
}
// 编译后css
h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: #222;
}
h1 {
font-size: 24px;
}
h2 {
font-size: 22px;
}
h3 {
font-size: 20px;
}
复制代码
@use
@use
是用来代替@import
。官方也说明了2022年会放弃对@import
的支持。@import
具体有什么劣势?例如
- 当我们import很多scss文件时,针对某一个mixin或者函数,我们不能够方便地知道它是来自于哪个文件
- 我们很容易产生命名冲突,导致不必要的错误。
- …
更多问题官方文档已经为我们详细介绍了。
具体用法
// variables.scss
$red: #dc3545;
// index.scss
@use 'variables';
.box {
color: variables.$red;
}
复制代码
我们还可以使用as
更名
// index.scss
@use 'variables' as v;
.box {
color: v.$red;
}
复制代码
又或者可以使用as *
,省略模板名。但是如果引入多个模板,几个模板有相同名称的mixin或函数的话,很容易有冲突。请确保你想这样做。
// index.scss
@use 'variables' as *;
.box {
color: $red;
}
复制代码
更多的就不再列举了,参照sass.bootcss.com/blog/the-mo…
额外
关于Visual Studio Code插件Live Sass Compiler可将Sass或Scss实时编译为CSS,这里推荐的插件github.com/ritwickdey/…,并没有跟sass更新而更新,很多新语法都会编译失败。建议使用另外一个github.com/glenn2223/v…
小结
合理地使用Sass,能够更好地管理我们的项目,同时便于维护。本次是个人使用Sass过程中的一些使用心得,之后会继续尝试更多更有实用的写法。若文章存在有误,欢迎各位指出。