likes
comments
collection
share

我在项目中如何使用 Sass

作者站长头像
站长
· 阅读数 18

背景

本文主要与大家分享,自己在项目中如何使用 sass 一些心得总结。

本文仅个人理解,可能存在对有些概念表述不当。

1. 使用 dart-sass 代替 node-sass

之前项目一直都使用 node-sass,但有一个让人头痛的点是:只要切换 node 版本,运行必报错 🤣。所以果断进行切换,主要 dart-sass 是官方推荐,提供了 dart-sass JS 编译版本 dart-sass(JS),方便集成。另外,dart-sass 将作为 SASS 开发团队的未来主要开发方向。而 node-sass 不再做新功能的版本迭代,会被逐渐边缘化。虽然 dart-sass(JS) 编译速度不如 node-sass,但表现上可以忽略不计。如果追求编译速度,可以直接使用 dart-sass,编译速度贼快 😀。

singsong: 本文中 dart-sass(JS) 表示 dart-sass JS 编译版本。项目中使用也是 dart-sass(JS)。

node-sass 区别

  • dart-sass(JS) : dart-sass JS 编译版本。
  • node-sass:node 调用 C++ 编写的 libsass SASS 编译器来编译 sass,依赖于 nodejs 版本。

2. 使用 Partials 加速编译

在创建 sass 样式文件时,命名以 _ 开头,会创建一个 sass partial 文件。如 _partial.scss,这样会让 sass 编译器知道该文件是个 partial,不应该输出 css 文件。如下是项目中的样式结构:

  • 样式结构

    src/styles
    ├── base
    │   ├── _animate.scss
    │   └── _reset.scss
    ├── lib
    │   └── _iconfont.scss
    ├── sass
    │   ├── _config.scss
    │   ├── _function.scss
    │   ├── _helper.scss
    │   ├── _index.scss
    │   ├── _layout.scss
    │   ├── _minxin.scss
    │   └── _theme.scss
    └── styles.scss
    
  • 组件样式结构

    ├── Home
    │   ├── Home.scss
    │   └── Home.tsx
    ├── User
    │   ├── User.scss
    │   ├── User.tsx
    │   └── UserSkeleton.tsx
    └── index.tsx
    

    singsong: 为什么组件样式没有采用 Partials,主要因为组件样式是一个独立结构。

3. 使用 @use@forward 组合代替 @import 更符合模块化组织方式

The Sass team discourages the continued use of the @import rule. Sass will gradually phase it out over the next few years, and eventually remove it from the language entirely. Prefer the @use rule instead. (Note that only Dart Sass currently supports @use. Users of other implementations must use the @import rule instead.)

Sass 团队不鼓励继续使用 @import。 Sass 将在未来几年逐步淘汰它,并最终将其从语言中完全删除。主推 @use。主要因为 @import 存在如下问题:

  • @import 破坏模块化组织方式。因为通过 @import 导入 variablesmixins、和 functions 都会变成全局可访问的。由于是全局的,还可能会导致命名冲突错误。
  • @extend 规则也是全局的,这使得很难预测将扩展哪些样式规则。
  • @import 也是一个 CSS 特性,两者之间的区别可能会让人感到困惑。
  • @import 允许多次导入同一个样式文件,这样不仅会影响编译速度,而且可能会产生一些冗余输出。
  • 无法定义下游样式表无法访问的私有成员或占位符选择器

新版模块系统及 @use 很好地解决了上述问题。如果想迁移到 @use,可以使用官方提供的迁移工具

@use

  • namespace(命名空间)避免全局导入。导入的成员都会挂载到 namespace<namespace>.<variable>, <namespace>.<function>(), 或 @include <namespace>.<mixin>()等。默认,模块的命名空间是其 URL 结尾去掉文件扩展名(和前缀 "_" 符)的部分。如 src/_corners.scss,命名空间为 corners

    singsong: 简单描述,就是文件名😄

    // src/_corners.scss
    $radius: 3px;
    
    @mixin rounded {
      border-radius: $radius;
    }
    
    // style.scss 引用 src/_corners.scss
    @use "src/corners";
    
    .button {
      // 导入的成员会挂载在 `corners`
      @include corners.rounded;
      padding: 5px + corners.$radius;
    }
    

    当然,有时也存在定制命名空间需求,语法为 @use "<url>" as <namespace>

    // src/_corners.scss
    $radius: 3px;
    
    @mixin rounded {
      border-radius: $radius;
    }
    
    // style.scss 引用 src/_corners.scss
    @use "src/corners" as c; // 定制命名空间为 "c"
    
    .button {
      @include c.rounded;
      padding: 5px + c.$radius;
    }
    

    如果引用时,觉得书写 namespace 麻烦,可以将成员挂载到全局上。此时与 @import 导入引用等效。语法:@use "<url>" as *该种方式需要注意命名冲突

    // src/_corners.scss
    $radius: 3px;
    
    @mixin rounded {
      border-radius: $radius;
    }
    
    // style.scss 引用 src/_corners.scss
    @use "src/corners" as *; // 无命名空间
    
    .button {
      @include rounded; // 直接引用
      padding: 5px + c.$radius;
    }
    
  • 私有成员

    Sass 提供了基于模块的私有成员,该成员只能在定义的文件内可访问。导出到其他引入样式文件是不可访问的。成员名以 "_""-" 开头的成员:_memberName-memberName

    // src/_corners.scss
    $-radius: 3px;
    
    @mixin rounded {
      border-radius: $-radius;
    }
    
    // style.scss
    @use "src/corners";
    
    .button {
      @include corners.rounded;
    
      // This is an error! $-radius isn't visible outside of `_corners.scss`.
      padding: 5px + corners.$-radius;
    }
    
  • 配置模块

    在定义变量时,结尾添加 !default,可让其变成可配置变量。在引入可配置模块时,使用 @use <url> with (<variable>: <value>, <variable>: <value>) 语法引用。指定的配置值会覆盖掉默认值。

    // _library.scss 添加 "!default" 指定为可配置变量
    $black: #000 !default;
    $border-radius: 0.25rem !default;
    $box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;
    
    code {
      border-radius: $border-radius;
      box-shadow: $box-shadow;
    }
    
    // style.scss 指定可配置变量的值
    @use 'library' with (
      $black: #222,
      $border-radius: 0.1rem
    );
    
  • 使用内置模块。sass 提供了 math, color, string, list, map, selector, and meta 等内置模块。在使用这些模块时,必须先导入

    @use 'sass:map';
    @use 'sass:string';
    $map-get: map.get(
      (
        'key': 'value',
      ),
      'key'
    );
    $str-index: string.index('string', 'i');
    
  • @use 加载解析

    • 不需明确加载模块的后缀,sass 会自动解析。如 @use "variables",会依次查找 variables.scssvariables.sass, 或 variables.css
    • sass 是按 url 解析模块不是按文件路径。分隔符只存在 "/"
    • sass 总是相对于当前目录进行查找,如果没有查找到,才按照 Load Paths 指定路径继续查找。
    • 在导入 Partials,可以省掉前缀 "_"

@forward

在将一个大文件分割到不同子文件中时,如果要重组,可使用 @forward 进行转发重组。假如现在有一些表格相关的样式表,希望将这些样式表的成员导入到同一个 namespace 下。

  • 重组样式 forms/_index.scss

    // forms/_index.scss
    @forward 'input';
    @forward 'textarea';
    @forward 'select';
    @forward 'buttons';
    
  • 引用,导入同一个 namespace 下

    // styles.scss
    @use 'forms'; // forms 命名空间
    

默认情况下,所有非私有成员都会被转发。我们还可以通过 showhide 关键字做进一步定制

// 只转发 "input" 模块下名为 `border()` mixin 和 `$border-color` 变量
@forward 'input' show border, $border-color;

// 转发 "buttons" 模块下除 `gradient()` function 外的所有成员
@forward 'buttons' hide gradient;

如果将多个不同的样式文件转发的到同一个 namespace 下,可能存在命名冲突的可能,此时可以通过指定前缀的方式解决 @forward "<url>" as <prefix>-*

// forms/_index.scss
// 假如 "input" 与 "buttons" 模块同时存在 `background()` mixin
@forward 'input' as input-*;
@forward 'buttons' as btn-*;

// style.scss
@use 'forms';
@include forms.input-background();
@include forms.btn-background();

4. 基于 webpack 注入全局样式

项目中样式结构:

src/styles
├── base
│   ├── _animate.scss
│   └── _reset.scss
├── lib
│   └── _iconfont.scss
├── sass
│   ├── _config.scss
│   ├── _function.scss
│   ├── _helper.scss
│   ├── _index.scss
│   ├── _layout.scss
│   ├── _minxin.scss
│   └── _theme.scss
└── styles.scss

其中 sass 目录下的样式都是 variablesmixins、和 functions 等一些常用的样式。并使用 @forward 转发重组到 sass/_index.scss

singsong: 这些样式文件不会生成 css 代码,都是些变量、辅助函数等。

// common styles

@forward './helper';
@forward './function';
@forward './config';
@forward './minxin';
@forward './theme';
@forward './layout';

在 react 组件中会常用引用这些样式。但引用前,必须先导入。感觉这样很麻烦!查阅一番,了解到 sass-loader 提供了 additionalData 可以解决这个麻烦。

singsong: sass-loader v9.0.0 以前使用的是 prependData 属性。

additionalData

Type: String|Function Default: undefined

Prepends Sass/SCSS code before the actual entry file. In this case, the sass-loader will not override the data option but just prepend the entry's content.

大意:sass-loader 会根据 additionalData 选项,会在每个加载的 sass 文件之前添加 additionalData 的值。

知道解决方法,就可以对 create-react-app 生成的开发环境做调整了。

  • eject 的情况(直接修改配置文件):

    const getStyleLoaders = (cssOptions, preProcessor) => {
      const loaders = [
        /* ..... */
      ].filter(Boolean);
      if (preProcessor) {
        loaders.push(
          {
            loader: require.resolve('resolve-url-loader'),
            options: {
              sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
              root: paths.appSrc,
            },
          },
          {
            loader: require.resolve(preProcessor),
            options: {
              sourceMap: true,
              additionalData: '@import "~@/styles/sass/index";',
            },
          }
        );
      }
      return loaders;
    };
    
  • eject的情况(基于 react-app-rewired 修改配置):

    module.exports function override(config, env) {
      const SASS_RULES_LEN = config.module.rules.length;
      const SASS_ONEOF_LEN = config.module.rules[SASS_RULES_LEN - 1].oneOf.length;
      // 为什么需要处理两项,因为有一项是对 CSS Module 的处理
      // 不同 webpack 版本,这里配置不太一样,请按实际情况自行配置。
      config.module.rules[SASS_RULES_LEN - 1].oneOf[SASS_ONEOF_LEN - 2].use[4].options.additionalData = '@use "~@/styles/sass/index" as *;';
      config.module.rules[SASS_RULES_LEN - 1].oneOf[SASS_ONEOF_LEN - 3].use[4].options.additionalData = '@use "~@/styles/sass/index" as *;';
      return config;
    }
    

    配置完,就可以愉快地在 src 目录下的任何地方使用了 sass 目录下任何声明了 😀。

5. stylelint 检测样式代码

项目中引入 stylelint 确保 sass 样式的规范。

相关包

配合 vscode,pre-commit,可让开发流程更加规范。

参考文章

转载自:https://juejin.cn/post/7137234474290380813
评论
请登录