Skip to content
文章目录

sass相关

scss 中的样式复用

通过@mixin进行样式复用

不传参 mixin

scss
@mixin center() {
  display: flex;
  justify-content: center;
  align-content: center;
}

调用

scss
.box {
  @include center;
  background: red;
}

传参的@mixin

scss
@mixin font($fontsize, $color) {
  font-size: $fontsize;
  color: $color;
}

调用

scss
p {
  text-align: center;
  @include font(0.28rem, #666);
}

@mixin 使用默认值

scss
@mixin font($fontsize: 14px, $color: red) {
  font-size: $fontsize;
  color: $color;
}

调用

scss
p {
  text-align: center;
  @include font;
}

if/else 使用

scss
$type: audi;
p {
  @if $type == benz {
    color: red;
  } @else if $type == mahindra {
    color: blue;
  } @else if $type == audi {
    color: green;
  } @else {
    color: black;
  }
}

编译后的 css 代码如下:

scss
p {
  color: green;
}

SASS 中变量的默认值

SASS 中定义的变量,后设置的值会覆盖旧的值。

scss
$color: red;
$color: blue;
.btn {
  color: $color;
}

编译后为:

scss
.btn {
  color: blue;
}

如果你编写了一个 UI 库提供 SASS 文件,可能会提供一些参数供用户使用时自定义。而在 SASS 组件内部,我们需要应用上用户设置的这些值。但是如果使用者没有自定义变量的值,那这些变量应该有自己的默认值。

利用前面提到的覆盖机制是不能实现的。因为无论是你在 @import UI 库之前设置还是之后,都不能影响这个导入文件中的值。如果你设置的值在导入之前,那么 UI 库中的变量因为在后面,所以你的设置会被覆盖而不起作用;如果你的设置在导入之后,那更加不起作用了。

假设这是 UI 中的样式文件:

scss
// _lib.scss
$color: red;
.btn {
  color: $color;
}

在另一个文件中使用,并且试图自定义变量的值:

scss
// page.scss
@import 'lib';
$color: blue;

scss
// page.scss
$color: blue;
@import 'lib';

两者编译结果均为:

scss
.btn {
  color: red;
}

!default

针对这种情况,SASS 提供了 !default 标识。将该标识应用于变量值后面,表示如果该变量没有在其他地方定义或即便定义了但值为 null,那此处设置的默认值才生效,否则使用其他地方设置的那个值。

将上面 _lib.scss 进行改造:

scss
$color: red !default;
.btn {
  color: $color;
}

使用:

scss
$color: blue;
@import 'lib';

注意非字符串变量的声明方式

scss
$color: blue;

注意: 需要将自定义的值先于 @import,否则也不生效。

此时编译结果将是想要的那样,应用上了外部自定义的变量值。

scss
.btn {
  color: blue;
}

SASS 为 CSS 类名称添加前缀

scss
$namespace: 'tb';

.#{$namespace}-myClass {
  background: pink !important;
}

.#{$namespace}-carousel-module {
  width: 25%;
}

注意在类名上面变量的引用方式为:

scss
#{$namespace}

注意字符串变量的声明方式为:

scss
$namespace: 'tb';

有了 css module 之后,为什么还要弄个命名空间防止类名冲突?

因为你开发了一个组件,使用了 class module,这只能防止,你的类名样式,对引入方类名样式的影响。但不能避免别人类名样式对你类名样式的影响.

比如:

你开发了一个按钮组件(good-button),发布到了npm仓库,你按钮组件里面
有一个`bg-red-color`的class,作用是将按钮背景颜色设置为纯红色(red)。
另外一个项目引入了你的组件,但他们的class名中也有个`bg-red-color`,
作用是将按钮背景颜色设置为粉红色(pink).当这两者以类似这种形式出现时.

<template>
    <div class='container'>
        <good-button>好按钮</good-button>
        <button class='bg-red-color'>粉红按钮</button>
    </container>
</template>

<style lang='scss' scoped>
.container{
    .bg-red-color{
        background-color: pink;
    }
}
</style>


此时good-button的.bg-red-color样式,有可能被覆盖为pink, 而预期应该是red.

此时怎么修复这个问题? 有两种方式

方式一: 给good-button再添加一个clss,通过权重方式,将good-button中的背景再改为red
方式二: 引入good-button这个项目放弃使用`bg-red-color`这个命名

方式二明显不合适.

方式一有一定的风险. 比如有天你升级了你的good-button,改变了他们的
html结构或使用的html标签,那此时引入good-button组件的这个项目,
之前做的样式权重覆盖修改,可能因此无效

所以最好的方式,就是你对外提供的组件,在 js 部分,提供一个可修改该名称空间的地方,样式部分使用 sass,且也使用名称空间作为 class 的前缀,此名称空间是一个 sass 变量,那其他人引入你组件发现有样式冲突的时候,就只要在 js 部分和 scss 部分都将你的 namespace 改了,那就不会有冲突了,后续你组件升级他们也不用担心样式冲突问题了

@use的基本用法

@use将取代@import,使 css,变量,mixin, 函数都可以在不同的样式表中复用。一个样式表文件就是一个模块,其命名空间会基于文件名自动生成,也可以进行自定义命名。变量、mixin 和函数会默认在该命名空间下使用。见下例:

scss
@use 'bootstrap' as b;

.element {
  background-color: b.$body-bg;
  @include b.float-left;
}

如果使用了as *,那么这一模块就处于全局命名空间,可以直接使用其中的变量和 mixin。但注意,如果多个模块暴露出的变量命名重复,并且都使用了as *,那么 sass 会报错[Error: This mixin is available from multiple global modules:]

scss
@use 'bootstrap' as *;

.element {
  @include float-left;
}

@use@import的区别

  • 不管使用了多少次样式表,@use都只会引入和执行一次。
  • 与全局使用相反,@use是有命名空间的,而且只在当前样式表中生效。
  • 或者_开头的命名空间被认为是私有的,不会被引入其他样式表。
scss
/* buttons.scss */
$_height: 20px;

/* tryuse.scss */
@use 'buttons';

.my-buttoon {
  background-color: buttons.$color;
  height: buttons.$_height;
}

@forward的基本用法

@forward语句可以引入另一个模块的所有变量、mixins 和函数,将它们直接作为当前模块的 API 暴露出去,而不会真的在当前模块增加代码。这样,库作者可以更好地在不同源文件之间拆分代码。不同于@use@forward不会给变量添加命名空间。

scss
/* bootstrap.scss */
@forward 'functions';
@forward 'variables';
@forward 'mixins';

注意,此时生成的 bootstrap.css 文件中,是不包含"functions"、“variables”、"mixins"代码的,也不能直接在 bootstrap.scss 文件中使用这些模块。而是需要在另一个文件中@import 或者@use bootstrap 模块,再去使用这些方法。bootstrap.scss 文件类似于一个传输中转站,把上下游的成员变量无缝连接起来。

注意,直接写在上游模块的样式仍然会被@forward 进来。见下例:

scss
/* upstream.scss */
... footer {
  height: pow(2, 3) * 1px;
  font-weight: map.get($font-weights, 'medium');
}

/* downstream.scss */
@forward 'upstream.scss';

/* 生成的downstream.css */
footer {
  height: 8px;
  font-weight: 500;
}

show/hide控制成员是否可见

scss
@forward 'functions' show color-yiq;
@forward 'functions' hide assert-ascending;

通过控制 show 和 hide,可以决定模块中的哪些成员对引入后的模板可见。对隐藏的变量,在下游文件中不可以使用,相当于模块私有成员。

给不同的子模块添加前缀

大多数情况下,一个样式库会存在一个入口文件index.scss,然后在index.scss中引入其他的子文件。这种结构类似于多合一模块。那么,如果要在某一文件中@forward多个子模块,就可以使用as子句,来使子模块下的成员自动带上前缀以区分。

scss
/* material/_index.scss */
@forward 'theme' as theme-*;
@forward 'func' as func-*;

/* downstream.scss */

@use 'material' as *;

p {
  color: $theme-white;
}
/* 或者,命名空间从父到子 */

@use 'material';

p {
  height: material.func-pow(4, 3) * 1px;
  color: material.$theme-white;
}

/* 也可以在多合一模块中为theme相关的变量重新定义值 */
@use 'material' with (
  $theme-primary: blue
);

/* 这等价于: */
@use 'material/theme' with (
  $primary: blue
);

sass 内置模块

sass 引入模块化后,还有一个很大的改变,就是把原来的内置函数归类到了内置模块中。这些内置模块包括: sass:math, sass:color, sass:string, sass:list, sass:map, sass:selector, and sass:meta。使用时需要先@use引用内置模块,才能使用模块中的方法。

scss
@use 'sass:color';

.button {
  $primary-color: #6b717f;
  color: $primary-color;
  border: 1px solid color.scale($primary-color, $lightness: 20%);
}

代码会被编译为:

scss
.button {
  color: #6b717f;
  border: 1px solid #878d9a;
}

这些模块被引入的时候需要加上命名空间,这样可以大大避免 sass 内置函数与用户自定义函数及 css 原生方法的命名冲突。此后,sass 新增函数也会更加方便。

sass 文件中引入其他样式文件

正确的做法(这种写法才是 scss 的语法规则)

scss
@import './tools/mixin/BEM.scss';

@import ./tools/mixin/BEM.scss;

虽然上面的@import方式是正确的 scss 语法,但@import语法在scss中可能会导致重复导入, 在scss中应该优先使用@use代替@import

scss
@use './tools/mixin/BEM.scss';

@use ./tools/mixin/BEM.scss;

不正确的做法(如下写法属于 css 的语法规则, 在 scss 中这种写法可能会导致错误或者非预期结果)

scss
@import url('./tools/mixin/_BEM.scss');

@import url(./tools/mixin/_BEM.scss);

关联文章

《vite 中使用 sass》

参考资料

vite 中 全局引入 scss 的坑

使用变量添加前缀,分别在 less/sass 中的实践

使用 LESS 或 SASS 为 CSS 类名称添加前缀

scss-else if 指令

scss 实用进阶-样式复用

一文详解 sass 新特性——模块