Webpack sass-loader

2023-05-22 09:18 更新

加載 Sass/SCSS 文件并將他們編譯為 CSS。

快速開始

首先,你需要安裝 sass-loader:

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

sass-loader 需要預先安裝 Dart Sass 或 Node Sass(可以在這兩個鏈接中找到更多的資料)。這可以控制所有依賴的版本, 并自由的選擇使用的 Sass 實現(xiàn)。

這樣可以控制所有依賴項的版本,并選擇要使用的 Sass 實現(xiàn)。

?? 我們推薦使用 Dart Sass。
? Node Sass 不能與 Yarn PnP 特性一起正常工作,并且不支持 @use rule。

將 sass-loader 、css-loader 與 style-loader 進行鏈式調(diào)用,可以將樣式以 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 節(jié)點
          'style-loader',
          // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
          'css-loader',
          // 將 Sass 編譯成 CSS
          'sass-loader',
        ],
      },
    ],
  },
};

最后通過你喜歡的方式運行 webpack。

解析 import 的規(guī)則

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 文件沒有用于導入相關(guān)文件的特殊語法,所以 Webpack 需要區(qū)分 bootstrap 和 ~bootstrap。 @import "style.scss" 和 @import "./style.scss"; 兩種寫法是相同的。

url(...) 的問題

由于 Sass 的實現(xiàn)沒有提供 url 重寫的功能,所以相關(guān)的資源都必須是相對于輸出文件(ouput)而言的。

  • 如果生成的 CSS 傳遞給了 css-loader,則所有的 url 規(guī)則都必須是相對于入口文件的(例如:main.scss)。
  • 如果僅僅生成了 CSS 文件,沒有將其傳遞給 css-loader,那么所有的 url 都是相對于網(wǎng)站的根目錄的。

第一種情況可能會帶來一些困擾。通常情況下我們希望相對路徑引用的解析是相對于聲明它的 .sass/.scss 文件(如同在 .css 文件中一樣)。

幸運的是,有兩種方法可以解決這個問題:

  • 將 resolve-url-loader 設置于 loader 鏈中的 sass-loader 之前,就可以重寫 url。
  • Library 作者一般都會提供變量,用來設置資源路徑。比如 bootstrap-sass 可以通過 $icon-font-path 進行設置。

配置選項

名稱 類型 默認值 Description
implementation {Object|String} sass 設置使用的 Sass 的實現(xiàn)。
sassOptions {Object|Function} Sass 實現(xiàn)的默認值 Sass 自身選項。
sourceMap {Boolean} compiler.devtool 啟用 / 禁用 source maps 的生成。
additionalData {String|Function} undefined 在實際的輸入文件之前添加 Sass /SCSS 代碼。
webpackImporter {Boolean} true 啟用 / 禁用默認的 Webpack importer。
warnRuleAsWarning {Boolean} false 將 @warn 規(guī)則視為 webpack 警告。

implementation

類型: ?Object | String? 默認值: sass

特殊的 implementation 選項確定要使用的 Sass 實現(xiàn)。

默認情況下,loader 將會根據(jù)你的依賴解析需要使用的實現(xiàn)。 只需將必需的實現(xiàn)添加到 package.json(sass 或 node-sass 包)中并安裝依賴項即可。

示例,此時 sass-loader 將會使用 sass (dart-sass)實現(xiàn):

package.json

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

示例,此時 sass-loader 將會使用 node-sass 實現(xiàn):

package.json

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

需注意同時安裝 node-sass 和 sass 的情況!默認情況下,sass-loader 會選擇 sass。 為了避免這種情況,你可以使用 implementation 選項。

implementation 選項可以以模塊的形式接受 sass(Dart 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'),
            },
          },
        ],
      },
    ],
  },
};

需要注意的是,當使用 sass(Dart Sass)時,由于異步回調(diào)的開銷,通常情況下同步編譯的速度是異步編譯速度的兩倍。 為了避免這種開銷,你可以使用 fibers 包從同步代碼中調(diào)用異步導入程序。

如果可能,我們會為小于 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 參數(shù)關(guān)閉自動注入的 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 實現(xiàn)的默認值

Dart Sass 或者 Node Sass 實現(xiàn)的選項。

?? dart-sass 的 charset 選項默認為 true,我們強烈建議你將其改為 false,因為 webpack 并不支持 utf-8 以外的文件。
?? indentedSyntax 選項值為 true,是對 sass 的擴展。
?? 像 data 和 file 這樣的選項是不可用的,且會被忽略。
? 我們堅決反對設置 outFile,sourceMapContents,sourceMapEmbed,sourceMapRoot 這些選項,因為當 sourceMap 是 true 時,sass-loader 會自動設置這些選項。
?? 可以使用 this.webpackLoaderContext 屬性訪問自定義 importer 中的 loader 上下文。

sass (dart-sass)和 node-sass 之間的選項略有不同。

在使用他們之前,請查閱有關(guān)文檔:

  • Dart Sass 文檔 提供了所有可用的 sass 選項。
  • Node Sass 文檔 提供了所有可用的 node-sass 選項。

Object

使用對象設置 Sass 實現(xiàn)的啟動選項。

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 實現(xiàn)設置不同的選項。

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              sassOptions: (loaderContext) => {
                // 有關(guān)可用屬性的更多信息 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 的值

開啟/關(guān)閉生成 source map。

默認情況下 source maps 的生成取決于 devtool 選項。 除 eval 和 false 之外的所有值都將開啟 source map 的生成。

? 如果為 true 將會忽略來自 sassOptions 的 sourceMap,sourceMapRoot,sourceMapEmbed,sourceMapContents 和 omitSourceMapUrl 選項。

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,
            },
          },
        ],
      },
    ],
  },
};
? 在極少數(shù)情況下,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 選項,而只是將它拼接在入口文件內(nèi)容之前。

當某些 Sass 變量取決于環(huán)境時,這非常有用:

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) => {
                // 有關(guān)可用屬性的更多信息 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

開啟 / 關(guān)閉默認的 Webpack importer。

在某些情況下,可以提高性能。但是請謹慎使用,因為 aliases 和以 ? 開頭的 @import 規(guī)則將不起作用。 你可以傳遞自己的 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,
            },
          },
        ],
      },
    ],
  },
};

warnRuleAsWarning

Type: Boolean Default: false

將 @warn 規(guī)則視為 webpack 警告。

?? 在下一個大版本發(fā)布中它將默認設置為 true。

style.scss

$known-prefixes: webkit, moz, ms, o;

@mixin prefix($property, $value, $prefixes) {
  @each $prefix in $prefixes {
    @if not index($known-prefixes, $prefix) {
      @warn "Unknown prefix #{$prefix}.";
    }

    -#{$prefix}-#{$property}: $value;
  }
  #{$property}: $value;
}

.tilt {
  // Oops, we typo'd "webkit" as "wekbit"!
  @include prefix(transform, rotate(15deg), wekbit ms);
}

給出的代碼將拋出 webpack 警告而不是日志。

要忽略不必要的警告,可以使用 ignoreWarnings 配置項。

webpack.config.js

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

示例

提取樣式表

對于生產(chǎn)版本,我們建議從 bundle 中提取 CSS,以便之后可以使 CSS/JS 資源并行加載。

從 bundle 中提取樣式表,有 2 種實用的方式:

  • mini-css-extract-plugin
  • extract-loader (簡單,專門針對 css-loader 的輸出)

webpack.config.js

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

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // 在開發(fā)過程中回退到 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

開啟/關(guān)閉 source map 的生成。

為了開啟 CSS source maps,需要將 sourceMap 選項作為參數(shù),傳遞給 sass-loader 和 css-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,
            },
          },
        ],
      },
    ],
  },
};

貢獻

如果你還沒有閱讀過我們的貢獻指南,請花一點時間閱讀它。

License

MIT


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號