sass-loader

npm node deps tests coverage chat size

加载 Sass/SCSS 文件并将他们编译为 CSS。

快速开始

首先,你需要安装 sass-loader

npm install sass-loader sass webpack --save-dev

sass-loader 需要预先安装 Dart SassNode Sass(可以在这两个链接中找到更多的资料)。这可以控制所有依赖的版本, 并自由的选择使用的 Sass 实现。

这样可以控制所有依赖项的版本,并选择要使用的 Sass 实现。

ℹ️ 我们推荐使用 Dart Sass

Node Sass 不能与 Yarn PnP 特性一起正常工作,并且不支持 @use rule

sass-loadercss-loaderstyle-loader 进行链式调用,可以将样式以 style 标签的形式插入 DOM 中,或者使用 mini-css-extract-plugin 将样式输出到独立的文件中。

然后将本 loader 添加到你的 Webpack 配置中。例如:

app.js

import './style.scss';

style.scss

$body-color: red;

body {
  color: $body-color;
}

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // 将 JS 字符串生成为 style 节点
          'style-loader',
          // 将 CSS 转化成 CommonJS 模块
          'css-loader',
          // 将 Sass 编译成 CSS
          'sass-loader',
        ],
      },
    ],
  },
};

最后通过你喜欢的方式运行 webpack

解析 import 的规则

Webpack 提供一种 解析文件的高级机制

sass-loader 使用 Sass 提供的 custom importer 特性,将所有 query 传递给 Webpack 解析引擎。 因此你可以从 node_modules 中引入 Sass modules。

@import "bootstrap";

~ 用法已被废弃,可以从代码中删除(我们建议这么做),但是我们会因为一些历史原因一直支持这种写法。 为什么你可以移除它呢?loader 首先会尝试以相对路径解析 @import,如果它不能被解析,loader 将会尝试在 node_modules 中解析 @import

在包名前加上 ~ 就会告诉 Webpack 在 modules 中进行查找。

@import "~bootstrap";

重要的是,只在前面加上 ~,因为~/ 将会解析到用户的主目录(home directory)。 因为 CSS 和 Sass 文件没有用于导入相关文件的特殊语法,所以 Webpack 需要区分 bootstrap~bootstrap@import "style.scss"@import "./style.scss"; 两种写法是相同的。

url(...) 的问题

由于 Saass 的实现没有提供 url 重写的功能,所以相关的资源都必须是相对于输出文件(ouput)而言的。

  • 如果生成的 CSS 传递给了 css-loader,则所有的 url 规则都必须是相对于入口文件的(例如:main.scss)。
  • 如果仅仅生成了 CSS 文件,没有将其传递给 css-loader,那么所有的 url 都是相对于网站的根目录的。

第一种情况可能会带来一些困扰。通常情况下我们希望相对路径引用的解析是相对于声明它的 .sass/.scss 文件(如同在 .css 文件中一样)。

幸运的是,有两种方法可以解决这个问题:

  • resolve-url-loader 设置于 loader 链中的 sass-loader 之前,就可以重写 url。
  • Library 作者一般都会提供变量,用来设置资源路径。比如 bootstrap-sass 可以通过 $icon-font-path 进行设置。

配置选项

名称类型默认值Description
implementation{Object|String}sass设置使用的 Sass 的实现。
sassOptions{Object|Function}Sass 实现的默认值Sass 自身选项。
sourceMap{Boolean}compiler.devtool启用 / 禁用 source maps 的生成。
additionalData{String|Function}undefined在实际的输入文件之前添加 Sass /SCSS 代码。
webpackImporter{Boolean}true启用 / 禁用默认的 Webpack importer。

implementation

类型: Object | String 默认值: sass

特殊的 implementation 选项确定要使用的 Sass 实现。

默认情况下,loader 将会根据你的依赖解析需要使用的实现。 只需将必需的实现添加到 package.jsonsassnode-sass 包)中并安装依赖项即可。

示例,此时 sass-loader 将会使用 sassdart-sass)实现:

package.json

{
  "devDependencies": {
    "sass-loader": "^7.2.0",
    "sass": "^1.22.10"
  }
}

示例,此时 sass-loader 将会使用 node-sass 实现:

package.json

{
  "devDependencies": {
    "sass-loader": "^7.2.0",
    "node-sass": "^5.0.0"
  }
}

需注意同时安装 node-sasssass 的情况!默认情况下,sass-loader 会选择 sass。 为了避免这种情况,你可以使用 implementation 选项。

implementation 选项可以以模块的形式接受 sassDart Sass)或 node-sass

Object

例如,为了使用 Dart Sass,你应该传递:

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              // `dart-sass` 是首选
              implementation: require('sass'),
            },
          },
        ],
      },
    ],
  },
};

String

例如,为了使用 Dart Sass,你应该传递:

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              // Prefer `dart-sass`
              implementation: require.resolve('sass'),
            },
          },
        ],
      },
    ],
  },
};

需要注意的是,当使用 sassDart Sass)时,由于异步回调的开销,通常情况下同步编译的速度是异步编译速度的两倍。 为了避免这种开销,你可以使用 fibers 包从同步代码中调用异步导入程序。

如果可能,我们会为小于 v16.0.0 的 Node.js 自动注入 fibers 软件包(设置 sassOptions.fiber)(当然需要你安装 fibers 包)。

Fibers 不兼容 Node.js v16.0.0 以及更高的版本(查看介绍)。

package.json

{
  "devDependencies": {
    "sass-loader": "^7.2.0",
    "sass": "^1.22.10",
    "fibers": "^4.0.1"
  }
}

你可以通过向 sassOptions.fiber 传递 false 参数关闭自动注入的 fibers 包。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              implementation: require('sass'),
              sassOptions: {
                fiber: false,
              },
            },
          },
        ],
      },
    ],
  },
};

你还可以通过一下代码传递 fiber

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              implementation: require('sass'),
              sassOptions: {
                fiber: require('fibers'),
              },
            },
          },
        ],
      },
    ],
  },
};

sassOptions

类型:Object|Function 默认值:Sass 实现的默认值

Dart Sass 或者 Node Sass 实现的选项。

ℹ️ dart-sasscharset 选项默认为 true,我们强烈建议你将其改为 false,因为 webpack 并不支持 utf-8 以外的文件。

ℹ️ indentedSyntax 选项值为 true,是对 sass 的扩展。

ℹ️ 像 datafile 这样的选项是不可用的,且会被忽略。

ℹ 我们坚决反对设置 outFilesourceMapContentssourceMapEmbedsourceMapRoot 这些选项,因为当 sourceMaptrue 时,sass-loader 会自动设置这些选项。

ℹ️ 可以使用 this.webpackLoaderContext 属性访问自定义 importer 中的 loader 上下文

sassdart-sass)和 node-sass 之间的选项略有不同。

在使用他们之前,请查阅有关文档:

Object

使用对象设置 Sass 实现的启动选项。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              sassOptions: {
                indentWidth: 4,
                includePaths: ['absolute/path/a', 'absolute/path/b'],
              },
            },
          },
        ],
      },
    ],
  },
};

Function

允许通过 loader 上下文为 Sass 实现设置不同的选项。

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              sassOptions: (loaderContext) => {
                // 有关可用属性的更多信息 https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === 'styles/foo.scss') {
                  return {
                    includePaths: ['absolute/path/c', 'absolute/path/d'],
                  };
                }

                return {
                  includePaths: ['absolute/path/a', 'absolute/path/b'],
                };
              },
            },
          },
        ],
      },
    ],
  },
};

sourceMap

类型:Boolean 默认值:取决于 compiler.devtool 的值

开启/关闭生成 source map。

默认情况下 source maps 的生成取决于 devtool 选项。 除 evalfalse 之外的所有值都将开启 source map 的生成。

ℹ 如果为 true 将会忽略来自 sassOptionssourceMapsourceMapRootsourceMapEmbedsourceMapContentsomitSourceMapUrl 选项。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
            },
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
};

ℹ 在极少数情况下,node-sass 会输出无效的 source maps(这是 node-sass 的 bug)。

为了避免这种情况,你可以尝试将 node-sass 更新到最新版本,或者可以尝试将 sassOptions 中的 outputStyle 选项设置为 compressed

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              sassOptions: {
                outputStyle: 'compressed',
              },
            },
          },
        ],
      },
    ],
  },
};

additionalData

类型:String|Function 默认值:undefined

在实际的文件之前要添加的 Sass / SCSS 代码。 在这种情况下,sass-loader 将不会覆盖 data 选项,而只是将它拼接在入口文件内容之前。

当某些 Sass 变量取决于环境时,这非常有用:

String

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              additionalData: '$env: ' + process.env.NODE_ENV + ';',
            },
          },
        ],
      },
    ],
  },
};

Function

Sync
module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              additionalData: (content, loaderContext) => {
                // 有关可用属性的更多信息 https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === 'styles/foo.scss') {
                  return '$value: 100px;' + content;
                }

                return '$value: 200px;' + content;
              },
            },
          },
        ],
      },
    ],
  },
};
Async
module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              additionalData: async (content, loaderContext) => {
                // More information about available properties https://webpack.js.org/api/loaders/
                const { resourcePath, rootContext } = loaderContext;
                const relativePath = path.relative(rootContext, resourcePath);

                if (relativePath === 'styles/foo.scss') {
                  return '$value: 100px;' + content;
                }

                return '$value: 200px;' + content;
              },
            },
          },
        ],
      },
    ],
  },
};

webpackImporter

类型:Boolean 默认值:true

开启 / 关闭默认的 Webpack importer。

在某些情况下,可以提高性能。但是请谨慎使用,因为 aliases 和以 开头的 @import 规则将不起作用。 你可以传递自己的 importer 来解决这个问题(参阅 importer docs)。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              webpackImporter: false,
            },
          },
        ],
      },
    ],
  },
};

示例

提取样式表

对于生产版本,我们建议从 bundle 中提取 CSS,以便之后可以使 CSS/JS 资源并行加载。

从 bundle 中提取样式表,有 2 种实用的方式:

webpack.config.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // 在开发过程中回退到 style-loader
          process.env.NODE_ENV !== 'production'
            ? 'style-loader'
            : MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      // 与 webpackOptions.output 中的选项相似
      // 所有的选项都是可选的
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
  ],
};

Source maps

开启/关闭 source map 的生成。

为了开启 CSS source maps,需要将 sourceMap 选项作为参数,传递给 sass-loadercss-loader

webpack.config.js

module.exports = {
  devtool: "source-map", // 任何类似于 "source-map" 的选项都是支持的
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
            },
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
};

如果你要在 Chrome 中编辑原始的 Sass 文件,建议阅读 这篇不错的博客。具体示例参考 test/sourceMap

贡献

如果你还没有阅读过我们的贡献指南,请花一点时间阅读它。

CONTRIBUTING

License

MIT