赞助 webpack,同时从官方商店购买衣服 所有收益将转到我们的 open collective

sass-loader

Loads a Sass/SCSS file and compiles it to CSS.

Use the css-loader or the raw-loader to turn it into a JS module and the mini-css-extract-plugin to extract it into a separate file. Looking for the webpack 1 loader? Check out the archive/webpack-1 branch.

安装

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

webpack 是 sass-loader 的 peerDependency, 并且还需要你预先安装 Node SassDart Sass。 这可以控制所有依赖的版本, 并选择要使用的 Sass 实现。

[Node Sass]:https://github.com/sass/node-sass [Dart Sass]:http://sass-lang.com/dart-sass

示例

通过将 style-loadercss-loader 与 sass-loader 链式调用,可以立刻将样式作用在 DOM 元素。

npm install style-loader css-loader --save-dev
// webpack.config.js
module.exports = {
    ...
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                "style-loader", // 将 JS 字符串生成为 style 节点
                "css-loader", // 将 CSS 转化成 CommonJS 模块
                "sass-loader" // 将 Sass 编译成 CSS,默认使用 Node Sass
            ]
        }]
    }
};

也可以直接将选项传递给 [Node Sass][] 或 [Dart Sass][]:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{
      test: /\.scss$/,
      use: [{
        loader: "style-loader"
      }, {
        loader: "css-loader"
      }, {
        loader: "sass-loader",
        options: {
          includePaths: ["absolute/path/a", "absolute/path/b"]
        }
      }]
    }]
  }
};

更多 Sass 可用选项,查看 Node Sass 文档 for all available Sass options.

By default the loader resolve the implementation based on your dependencies. Just add required implementation to package.json (node-sass or sass package) and install dependencies.

Example where the sass-loader loader uses the sass (dart-sass) implementation:

package.json

{
   "devDependencies": {
      "sass-loader": "*",
      "sass": "*"
   }
}

Example where the sass-loader loader uses the node-sass implementation:

package.json

{
   "devDependencies": {
      "sass-loader": "*",
      "node-sass": "*"
   }
}

Beware the situation when node-sass and sass was installed, by default the sass-loader prefers node-sass, to avoid this situation use the implementation option.

The special implementation option determines which implementation of Sass to use. It takes either a [Node Sass][] or a [Dart Sass][] module. For example, to use Dart Sass, you'd pass:

// ...
    {
        loader: "sass-loader",
        options: {
            implementation: require("sass")
        }
    }
// ...

Note that when using Dart Sass, synchronous compilation is twice as fast as asynchronous compilation by default, due to the overhead of asynchronous callbacks. To avoid this overhead, you can use the fibers package to call asynchronous importers from the synchronous code path. To enable this, pass the Fiber class to the fiber option:

// webpack.config.js
const Fiber = require('fibers');

module.exports = {
    ...
    module: {
        rules: [{
            test: /\.scss$/,
            use: [{
                loader: "style-loader"
            }, {
                loader: "css-loader"
            }, {
                loader: "sass-loader",
                options: {
                    implementation: require("sass"),
                    fiber: Fiber
                }
            }]
        }]
    }
};

##

通常,生产环境下比较推荐的做法是,使用 mini-css-extract-plugin 将样式表抽离成专门的单独文件。这样,样式表将不再依赖于 JavaScript:

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

module.exports = {
    ...
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                // fallback to style-loader in development
                process.env.NODE_ENV !== 'production' ? 'style-loader' : MiniCssExtractPlugin.loader,
                "css-loader",
                "sass-loader"
            ]
        }]
    },
    plugins: [
        new MiniCssExtractPlugin({
            // Options similar to the same options in webpackOptions.output
            // both options are optional
            filename: "[name].css",
            chunkFilename: "[id].css"
        })
    ]
};

用法

导入(import)

webpack 提供一种解析文件的高级的机制。sass-loader 使用 Sass 的 custom importer 特性,将所有的 query 传递给 webpack 的解析引擎(resolving engine)。只要它们前面加上 ~,告诉 webpack 它不是一个相对路径,这样就可以 import 导入 node_modules 目录里面的 sass 模块:

@import "~bootstrap/dist/css/bootstrap";

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

url(...) 的问题

由于 Sass/libsass 并没有提供url rewriting 的功能,所以所有的链接资源都是相对输出文件(output)而言。

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

第二种情况可能会带来一些问题。正常情况下我们期望相对路径的引用是相对于 .scss 去解析(如同在 .css 文件一样)。幸运的是,有2个方法可以解决这个问题:

提取样式表

使用 webpack 打包 CSS 有许多优点,在开发环境,可以通过 hashed urls 或 模块热替换(HMR) 引用图片和字体资源。而在线上环境,使样式依赖 JS 执行环境并不是一个好的实践。渲染会被推迟,甚至会出现 FOUC,因此在最终线上环境构建时,最好还是能够将 CSS 放在单独的文件中。

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

Source maps

要启用 CSS source map,需要将 sourceMap 选项作为参数,传递给 sass-loadercss-loader。此时webpack.config.js 如下:

module.exports = {
    ...
    devtool: "source-map", // any "source-map"-like devtool is possible
    module: {
        rules: [{
            test: /\.scss$/,
            use: [{
                loader: "style-loader", options: {
                    sourceMap: true
                }
            }, {
                loader: "css-loader", options: {
                    sourceMap: true
                }
            }, {
                loader: "sass-loader", options: {
                    sourceMap: true
                }
            }]
        }]
    }
};

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

环境变量

如果你要将 Sass 代码放在实际的入口文件(entry file)之前,可以设置 data 选项。此时 sass-loader 不会覆盖 data 选项,只会将它拼接在入口文件的内容之前。当 Sass 变量依赖于环境时,这一点尤其有用。

{
    loader: "sass-loader",
    options: {
        data: "$env: " + process.env.NODE_ENV + ";"
    }
}

The data option supports Function notation:

{
    loader: "sass-loader",
    options: {
        data: (loaderContext) => {
          // More information about avalaible options https://webpack.js.org/api/loaders/
          const { resourcePath, rootContext } = loaderContext;
          const relativePath = path.relative(rootContext,resourcePath);

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

          return "$value: 200px;"
        }
    }
}

注意:由于代码注入, 会破坏整个入口文件的 source map。通常一个简单的解决方案是,多个 Sass 文件入口。

维护人员


Johannes Ewald


Jorik Tangelder


Kiran

License

MIT