Sass中你不清楚的小细节-持续更新

2021-11-15 14:41:48 浏览数 (1)

sass

随着css工程化的普及,sass在前端工程中越来越举足轻重。当然sass并不局限于管理css全局变量、mixin之类的"脏活累活"。

这篇文章会跟随工程化前端一步一步记录sass中那些不为人知(我刚刚会)但是又非常使用的小技巧分享给大家。

长期更新奥~建议大家收藏随时观看。

内置函数

sass官网提供了很多实用的内置函数,当然目前我也是在一步一步探索这些函数。目前我会将常用到的内置函数以及场景分享给大家使用。

darken内置函数

定义

lighten()darken()两个函数都是围绕颜色的亮度值做调整的,其中lighten()函数会让颜色变得更亮,与之相反的darken()函数会让颜色变得更暗。这个亮度值可以是0~1之间。

代码语言:javascript复制
lighten(#fff,10%) //原色的亮度值 增加百分之10亮度
draken(#fff,10%) // 在原色的基础上 减少百分之10亮度
复制代码
应用场景

lightendarken这两个内置函数经常被用到元素被hover/focus时,当我们hover,一个元素的时候。此时并不希望改变这个元素的色值,但是又想要用户感知到鼠标停留在这个元素上。此时这两个内置函数就发挥了他们的作用了。

代码语言:javascript复制
@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函数就是针对饱和度操作的内置方法。

代码语言:javascript复制
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操作符给用户提示一些警告内容输出在控制台。

代码语言:javascript复制
@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

If keys is empty, returns whether map contains a value associated with

代码语言:javascript复制
$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,...)

简单来说就是通过keymap中取到对应的value

代码语言:javascript复制
$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命令,但cssimport命令每次调用都会创建一个额外的html请求,但scssimport命令是编译时将文件包含在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文件。

变量声明globaldefault

!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在局部作用域中去覆盖一个全局变量的值

代码语言:javascript复制
#main {
  $width: 5em !global;
  width: $width;
}

#sidebar {
  // 同样可以使用$width全局变量
  width: $width;
}
复制代码

编译为

代码语言:javascript复制
#main {
  width: 5em;
}

#sidebar {
  width: 5em;
}
复制代码

mixin

参数变量...

有时,不能确定混合指令需要使用多少个参数,比如一个关于 box-shadow 的混合指令不能确定有多少个 'shadow' 会被用到。这时,可以使用参数变量 声明(写在参数的最后方)告诉 Sass 将这些参数视为值列表处理.

其实就类似于js中的...rest运算符。

代码语言:javascript复制
@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使用。

代码语言:javascript复制
@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 可以用 表示,所以上面的例子可以写成:

代码语言:javascript复制
// = 简写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选择符

代码语言:javascript复制
.parent { ... }
.child { ... }
复制代码

@at-root同样也可以当作一个作用域给多个选择器去使用:

代码语言:javascript复制
.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的嵌套:

代码语言:javascript复制
@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 ):跳出作用域嵌套规则。

  1. @at-root(without: rule)

rule 关键词只能跳出选择器嵌套,不能跳出 @media@support

  1. @at-root(without: media)

  可以跳出 @media ,但是没有跳出父级选择器

  1. @at-root(without: support)

@at-root(without: support)@at-root(without: media) 相似,只是跳出的是 @support。

  1. @at-root(without: all)

@at-root(without: all) 是跳出所的指令和规则,如上面的代码里 @at-root(without: media rule) 我们可以换成 @at-root(without: all),效果是一样的。

@each in

maps数据格式

首先我们来说说在scss中定义类似js中的对象。

代码语言:javascript复制
$map: (key1: value1, key2: value2, key3: value3);
复制代码

我们通过()就可以定义了。 比如这样的Maps结构定义。

代码语言:javascript复制
$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 作用于值列表中的每一个项目,然后输出结果,例如:

代码语言:javascript复制
@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.querySelectorElement.querySelectorAllElement.closest时,:scope是指调用这些方法的元素。例如,document.body.querySelector(":scope")返回body元素。尽管CSS对 :scope的支持已被移除,但 :scope 的这种用法仍然被支持

使用:scope选择器可以匹配对应的方法比如element.querySelector(':scope')则会返回element元素本身。

需要主要的是:scope伪类在css中已经不被大多数浏览器支持,甚至已经废弃。但是在js这些方法中仍然被主流浏览器支持。

0 人点赞