sass
随着css
工程化的普及,sass
在前端工程中越来越举足轻重。当然sass
并不局限于管理css
全局变量、mixin
之类的"脏活累活"。
这篇文章会跟随工程化前端一步一步记录sass
中那些不为人知(我刚刚会)但是又非常使用的小技巧分享给大家。
长期更新奥~建议大家收藏随时观看。
内置函数
sass
官网提供了很多实用的内置函数,当然目前我也是在一步一步探索这些函数。目前我会将常用到的内置函数以及场景分享给大家使用。
darken
内置函数
定义
lighten()
和darken()
两个函数都是围绕颜色的亮度值做调整的,其中lighten()
函数会让颜色变得更亮,与之相反的darken()
函数会让颜色变得更暗。这个亮度值可以是0~1
之间。
lighten(#fff,10%) //原色的亮度值 增加百分之10亮度
draken(#fff,10%) // 在原色的基础上 减少百分之10亮度
复制代码
应用场景
lighten
和darken
这两个内置函数经常被用到元素被hover/focus
时,当我们hover
,一个元素的时候。此时并不希望改变这个元素的色值,但是又想要用户感知到鼠标停留在这个元素上。此时这两个内置函数就发挥了他们的作用了。
@mixin button-type(
$background,
$border,
$color,
$hover-background: lighten($background, 7.5),
$hover-border: lighten($border, 10%),
$hover-color: $color
) {
color: $color;
background: $background;
border-color: $border;
&:hover {
color: $hover-color;
background: $hover-background;
border-color: $hover-border;
}
&:focus,
&.is-focus {
color: $hover-color;
background: $hover-background;
border-color: $hover-border;
}
&[disabled],
&.is-disabled {
color: $color;
background: $background;
border-color: $border;
}
}
复制代码
desaturate
饱和度(Saturation)是指色彩的纯度,饱和度越高色彩越纯越浓,饱和度越低则色彩变灰变淡。
sass
中的desaturate
函数就是针对饱和度操作的内置方法。
desaturate($color, $amount) //=> color
复制代码
使color饱和度降低, 在amount必须之间的数字0%和100%(包含)。
@warn
When writing mixins and functions, you may want to discourage users from passing certain arguments or certain values. They may be passing legacy arguments that are now deprecated, or they may be calling your API in a way that’s not quite optimal.
编写MixIn和函数时,您可能希望劝阻用户传递某些参数或某些值。 他们可能正在传递现在已弃用的传统参数,或者他们可能会以不太最佳的方式调用您的API。
简单来说在mixin
或者function
内部,我们可以通过@warn
操作符给用户提示一些警告内容输出在控制台。
@mixin prefix($property, $value, $prefixes) {
@each $prefix in $prefixes {
@if not index($known-prefixes, $prefix) {
@warn "Unknown prefix #{$prefix}.";
}
-#{$prefix}-#{$property}: $value;
}
#{$property}: $value;
}
复制代码
控制台会提示
代码语言:javascript复制Warning: Unknown prefix wekbit.
example.scss 6:7 prefix()
example.scss 16:3 root stylesheet
复制代码
Inspect()
断电函数
其实Inspect()
函数用的比较少,主要是用来做校验类型的。
Inspect(...)
表达式中的内容如果是正常会返回对应的内容,如果发生错误则会弹出一个错误提示。
Map
相关内容
Map-has-key
代码语言:javascript复制If keys is empty, returns whether map contains a value associated with
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
map.has-key($font-weights, "regular"); // true
map.has-key($font-weights, "bolder"); // false
复制代码
map.has-key()
在scss
中的条件判断时应用场景特别多。
比如下方这段代码
代码语言:javascript复制$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;
$--breakpoints: (
'xs' : (max-width: $--sm - 1),
'sm' : (min-width: $--sm),
'md' : (min-width: $--md),
'lg' : (min-width: $--lg),
'xl' : (min-width: $--xl)
);
@mixin res($key, $map: $--breakpoints) {
// 循环断点Map,如果存在则返回
@if map.has-key($map, $key) {
@media only screen and #{inspect(map-get($map, $key))} {
@content;
}
} @else {
@warn "Undefeined points: `#{$map}`";
}
}
复制代码
map.get(map,k1,k2,...)
简单来说就是通过key
在map
中取到对应的value
$config: (a: (b: (c: d)));
map.get($config, a, b, c); // d
复制代码
占位符选择器%
作用
定义
Sass 额外提供了一种特殊类型的选择器:占位符选择器 (placeholder selector)。与常用的 id 与 class 选择器写法相似,只是 #
或 .
替换成了 %
。
比如:
代码语言:javascript复制%heading {
margin-top: 0; // 1
margin-bottom: $headings-margin-bottom;
font-family: $headings-font-family;
font-style: $headings-font-style;
font-weight: $headings-font-weight;
line-height: $headings-line-height;
color: $headings-color;
}
复制代码
应用场景
其实使用%
在大多数(所有)场景下,我的理解就是和@mixin
是一样的效果。使用%
占位符选择器的样式,只能被@extend
进行调用。
需要注意的是,如果使用占位符选择器
%
定义的样式,单独使用的时候(未通过extend
)进行调用,那么这段样式是不会编译到css
的输出结果之后的。
Partials import
定义
和css
类似scss
支持@import
命令,但css
的import
命令每次调用都会创建一个额外的html
请求,但scss
的import
命令是编译时将文件包含在css
中,不需要额外发起请求。
如果我们需要导入 SCSS 或者 Sass 文件,但又不希望将其编译为 CSS,只需要在文件名前添加下划线,这样会告诉 Sass 不要单独编译这些文件,但导入语句中却不需要添加下划线。
简单来说,项目目录中的所有scss
文件在编译阶段都会被编译成为一个个css
文件。
但是对于一个公用样式文件,此时我们并不需要将它编译成为单独的css
文件,而是希望将公用文件中的样式插入到对应引入样式文件中,我们只需要在引入它的文件中将它编译进入引入的css
文件中就可以。
此时给文件名称以_
开头就可以告诉scss
在编译阶段并不会将它编译成为单独的css
文件,而是仅仅会将它的样式编译进入引入它的样式文件中去。
比如一个文件夹两个 scss 文件,一个 root.scss,一个 _vars.scss。
代码语言:javascript复制// 第一个 scss 文件夹名 -o 是输出文件夹名称``npx node-sass scss -o output``// 只会有一个文件生成
rendering Complete, saving .css file...
Wrote CSS to /Users/liusha/Public/vikingship/output/root.css //将 _vars 该名称为 vars.scss 再执行一遍
Rendering Complete, saving .css file...
Wrote CSS to /Users/liusha/Public/vikingship/output/root.css
Rendering Complete, saving .css file...
Wrote CSS to /Users/liusha/Public/vikingship/output/vars.css
会有两个文件生成
复制代码
应用场景
这在组件库的开发中是非常有用的,定义单独组件的样式文件以Partials import
进行定义,不单独打包成为css文件,在最终导入的样式文件中统一进行合并管理而不打包出单独的css
文件。
变量声明global
与default
!default
默认变量
可以在变量的结尾添加
!default
给一个未通过!default
声明赋值的变量赋值,此时,如果变量已经被赋值,不会再被重新赋值,但是如果变量还没有被赋值,则会被赋予新的值。
比如这样一段代码:
代码语言:javascript复制$color:red;
$color:blue !default;
.modules-a {
color: $color; // red
}
复制代码
我们可以看到即使是先声明的red
,因为blue !default
,所以红色覆盖了蓝色。!default
声明变量的意思就是说如果项目中存在相同的声明则优先使用别的声明,如果不存在则使用!default
的值,可以理解为默认值。
!global
全局声明
变量支持块级作用域,嵌套规则内定义的变量只能在嵌套规则内使用(局部变量),不在嵌套规则内定义的变量则可在任何地方使用(全局变量)。将局部变量转换为全局变量可以添加
!global
声明
在scss
中我们都清楚局部变量的定义是无法影响同名的global
变量的。但是我们可以通过!global
在局部作用域中去定义一个全局都可以使用的变量。 同样也可以通过!default
在局部作用域中去覆盖一个全局变量的值。
#main {
$width: 5em !global;
width: $width;
}
#sidebar {
// 同样可以使用$width全局变量
width: $width;
}
复制代码
编译为
代码语言:javascript复制#main {
width: 5em;
}
#sidebar {
width: 5em;
}
复制代码
mixin
参数变量...
有时,不能确定混合指令需要使用多少个参数,比如一个关于
box-shadow
的混合指令不能确定有多少个 'shadow' 会被用到。这时,可以使用参数变量…
声明(写在参数的最后方)告诉 Sass 将这些参数视为值列表处理.
其实就类似于js
中的...
rest运算符。
@mixin box-shadow($shadows...) {
-moz-box-shadow: $shadows;
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
}
.shadows {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}
复制代码
编译后:
代码语言:javascript复制.shadowed {
-moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
-webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
}
复制代码
使用
,
分隔为.shadowed
元素添加多个阴影。
@content
-- 向混合样式中导入内容
在引用混合样式
mixin
的时候,可以先将一段代码导入到混合指令中,然后再输出混合样式,额外导入的部分将出现在@content
标志的地方
比如这样的代码,我们在include
中填充了对应的样式,在mixin
中可以通过@content
使用。
@mixin apply-to-ie6-only {
* html {
@content;
}
}
@include apply-to-ie6-only {
#logo {
background-image: url(/logo.gif);
}
}
复制代码
编译为
代码语言:javascript复制// mixin中接受了include 可以理解为插槽
* html #logo {
background-image: url(/logo.gif);
}
复制代码
为便于书写,@mixin
可以用 =
表示,而 @include
可以用
表示,所以上面的例子可以写成:
// = 简写mixin
=apply-to-ie6-only
* html
@content
// 简写include
apply-to-ie6-only
#logo
background-image: url(/logo.gif)
复制代码
注意: 当 @content
在指令中出现过多次或者出现在循环中时,额外的代码将被导入到每一个地方。
@at-root
常规用法
@at-root指令可以使一个或多个规则被限定输出在文档的根层级上,而不是被嵌套在其父选择器下。
比如
代码语言:javascript复制.parent {
...
@at-root .child { ... }
}
复制代码
编译之后.child
并不会嵌套在任何规则之下,因为使用了@at-root
选择符
.parent { ... }
.child { ... }
复制代码
@at-root
同样也可以当作一个作用域给多个选择器去使用:
.parent {
...
@at-root {
.child1 { ... }
.child2 { ... }
}
.step-child { ... }
}
复制代码
编译后:
代码语言:javascript复制.parent { ... }
.child1 { ... }
.child2 { ... }
.parent .step-child { ... }
复制代码
支持参数
@at-root (without: ...) and @at-root (with: ...)
默认使用@at-root
不传递任何时,他的作用为跳出选择器的作用域嵌套,当然可以传递参数去使用。
比如下面的@at-root
意为跳出@media
的嵌套:
@media print {
.page {
width: 8in;
@at-root (without: media) {
color: red;
}
}
}
复制代码
代码语言:javascript复制@media print {
.page {
width: 8in;
}
}
.page {
color: red;
}
复制代码
默认 @at-root
只会跳出选择器嵌套,而不能跳出@media
或 @support
,如果要跳出这两种,则需使用 @at-root(without: media)
,@at-root(without: support)
。这个语法的关键词有
四个:
all
(表示所有)rule
(表示常规,默认行为。media
(表示media
)support
(表示support
)。
我们默认的 @at-root
其实就是 @at-root( without: rule )
:跳出作用域嵌套规则。
@at-root(without: rule)
rule
关键词只能跳出选择器嵌套,不能跳出 @media
和 @support
@at-root(without: media)
可以跳出 @media
,但是没有跳出父级选择器
@at-root(without: support)
@at-root(without: support)
和 @at-root(without: media)
相似,只是跳出的是 @support。
@at-root(without: all)
@at-root(without: all)
是跳出所的指令和规则,如上面的代码里 @at-root(without: media rule)
我们可以换成 @at-root(without: all)
,效果是一样的。
@each in
maps数据格式
首先我们来说说在scss
中定义类似js
中的对象。
$map: (key1: value1, key2: value2, key3: value3);
复制代码
我们通过()
就可以定义了。 比如这样的Maps
结构定义。
$blue: #0d6efd !default;
$indigo: #6610f2 !default;
$purple: #6f42c1 !default;
$pink: #d63384 !default;
$red: #dc3545 !default;
$orange: #fd7e14 !default;
$yellow: #fadb14 !default;
$green: #52c41a !default;
$teal: #20c997 !default;
$cyan: #17a2b8 !default;
$primary: $blue !default;
$secondary: $gray-600 !default;
$success: $green !default;
$info: $cyan !default;
$warning: $yellow !default;
$danger: $red !default;
$light: $gray-100 !default;
$dark: $gray-800 !default;
$theme-colors: (
'primary': $primary,
'secondary': $secondary,
'success': $success,
'info': $info,
'warning': $warning,
'danger': $danger,
'light': $light,
'dark': $dark,
);
复制代码
我们定义了一个maps
的主题,分别存在对应的名称和对应的颜色值。
@each in
@each 指令的格式是 var in <list>, length 或者 name,而 <list> 是一连串的值,也就是值列表。
"数组"迭代
@each
将变量 $var
作用于值列表中的每一个项目,然后输出结果,例如:
@each $animal in puma, sea-slug, egret, salamander {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
}
}
复制代码
编译为
代码语言:javascript复制.puma-icon {
background-image: url('/images/puma.png'); }
.sea-slug-icon {
background-image: url('/images/sea-slug.png'); }
.egret-icon {
background-image: url('/images/egret.png'); }
.salamander-icon {
background-image: url('/images/salamander.png'); }
复制代码
此时类似于
js
中的数组迭代。
对象迭代
当然@each val,key in maps,也支持"迭代"一个对象(maps)。 比如:
代码语言:javascript复制@each $key, $value in $theme-colors {
.#{$prefix}-icon--#{$key} {
color: $value;
}
}
复制代码
这样我们就迭代了上边定义的
$theme-colors
这个对象,并且取得了他的key
,value
。
多个值迭代
The @each directive can also use multiple variables, as in @each var1,var1, var1,var2, ... in . If is a list of lists, each element of the sub-lists is assigned to the respective variable. For example:
@each 指令也可以使用多个变量,如@each var1, var2, ... in 。如果是列表列表,则子列表的每个元素都分配给相应的变量。例如
代码语言:javascript复制@each $animal, $color, $cursor in (puma, black, default),
(sea-slug, blue, pointer),
(egret, white, move) {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
border: 2px solid $color;
cursor: $cursor;
}
}
复制代码
is compiled to:
代码语言:javascript复制.puma-icon {
background-image: url('/images/puma.png');
border: 2px solid black;
cursor: default; }
.sea-slug-icon {
background-image: url('/images/sea-slug.png');
border: 2px solid blue;
cursor: pointer; }
.egret-icon {
background-image: url('/images/egret.png');
border: 2px solid white;
cursor: move; }
复制代码
css
:scope
选择器
偶然在写jest
中看到别人使用xxx.querySelect(':scope')
。感到比较新奇随机查阅记录一番。
在JavaScript中,当用于
Element.querySelector
,Element.querySelectorAll
或Element.closest
时,:scope
是指调用这些方法的元素。例如,document.body.querySelector(":scope")
返回body元素。尽管CSS对:scope
的支持已被移除,但:scope
的这种用法仍然被支持
使用:scope
选择器可以匹配对应的方法比如element.querySelector(':scope')
则会返回element
元素本身。
需要主要的是:scope
伪类在css
中已经不被大多数浏览器支持,甚至已经废弃。但是在js
这些方法中仍然被主流浏览器支持。