参考网址

官网
参考一
参考二

作用

loader 让 webpack 能够去处理那些非 JavaScript 文件。

使用

在你的应用程序中,有三种使用 loader 的方式:

  • 配置(推荐):在 webpack.config.js 文件中指定 loader。
  • 内联:在每个 import 语句中显式指定 loader。
  • CLI:在 shell 命令中指定它们。

配置

以scss文件为例,在webpack.config.js

  1. module.exports = {
  2. module: {
  3. rules: [
  4. {
  5. test: /\.scss/,
  6. use: [
  7. 'style-loader',
  8. {
  9. loader: 'css-loader',
  10. options: {
  11. minimize: true
  12. }
  13. },
  14. 'sass-loader'
  15. ]
  16. }
  17. ]
  18. }
  19. };

内联

可以在 import 语句中指定 loader。使用 ! 将资源中的 loader 分开。分开的每个部分都相对于当前目录解析。

  1. import Styles from 'style-loader!css-loader?modules!./styles.css';

通过前置所有规则及使用 !,可以对应覆盖到配置中的任意 loader。
选项可以传递查询参数,例如 ?key=value&foo=bar,或者一个 JSON 对象,例如 ?{“key”:”value”,”foo”:”bar”}。

CLI

  1. webpack --module-bind 'css=style-loader!css-loader'

对 .css 文件使用 style-loader 和 css-loader。

loader简单示例

  1. module.exports = function(source) {
  2. // source 为 compiler 传递给 Loader 的一个文件的原内容
  3. // 该函数需要返回处理后的内容,这里简单起见,直接把原内容返回了,相当于该 Loader 没有做任何转换
  4. return source;
  5. };

参数获取

options获取

在最上面处理 SCSS 文件的 Webpack 配置中,给 css-loader 传了 options 参数,以控制 css-loader。
如何在自己编写的 Loader 中获取到用户传入的 options 呢?需要这样做

  1. const loaderUtils = require('loader-utils');
  2. module.exports = function(source) {
  3. // 获取到用户给当前 Loader 传入的 options
  4. const options = loaderUtils.getOptions(this);
  5. return source;
  6. };

返回其他结果

上面的 Loader 都只是返回了原内容转换后的内容,但有些场景下还需要返回除了内容之外的东西。
例如以用 babel-loader 转换 ES6 代码为例,它还需要输出转换后的 ES5 代码对应的 Source Map,以方便调试源码。
为了把 Source Map 也一起随着 ES5 代码返回给 Webpack,可以这样写:

  1. module.exports = function(source) {
  2. // 通过 this.callback 告诉 Webpack 返回的结果
  3. this.callback(null, source, sourceMaps);
  4. // 当你使用 this.callback 返回内容时,该 Loader 必须返回 undefined,
  5. // 以让 Webpack 知道该 Loader 返回的结果在 this.callback 中,而不是 return 中
  6. return;
  7. };

this.callback
  1. this.callback(
  2. err: Error | null,
  3. content: string | Buffer,
  4. sourceMap?: SourceMap,
  5. meta?: any
  6. );
  1. 第一个参数必须是 Error 或者 null
  2. 第二个参数是一个 string 或者 Buffer。
  3. 可选的:第三个参数必须是一个可以被这个模块解析的 source map。
  4. 可选的:第四个选项,会被 webpack 忽略,可以是任何东西(例如一些元数据)。

注意:如果这个函数被调用的话,你应该返回 undefined 从而避免含糊的 loader 结果。
**

同步异步

同步

无论是 return 还是 this.callback 都可以同步地返回转换后的 content 内容:

  1. module.exports = function(content, map, meta) {
  2. return someSyncOperation(content);
  3. };
  1. module.exports = function(content, map, meta) {
  2. this.callback(null, someSyncOperation(content), map, meta);
  3. return; // 当调用 callback() 时总是返回 undefined
  4. };

异步

对于异步 loader,使用 this.async 来获取 callback 函数:

  1. module.exports = function(content, map, meta) {
  2. var callback = this.async();
  3. someAsyncOperation(content, function(err, result, sourceMaps, meta) {
  4. if (err) return callback(err);
  5. callback(null, result, sourceMaps, meta);
  6. });
  7. };

处理二进制数据

在默认的情况下,Webpack 传给 Loader 的原内容都是 UTF-8 格式编码的字符串。
但有些场景下 Loader 不是处理文本文件,而是处理二进制文件,例如 file-loader,就需要 Webpack 给 Loader 传入二进制格式的数据。
为此,你需要这样编写 Loader:

  1. module.exports = function(source) {
  2. // 在 exports.raw === true 时,Webpack 传给 Loader 的 source 是 Buffer 类型的
  3. source instanceof Buffer === true;
  4. // Loader 返回的类型也可以是 Buffer 类型的
  5. // 在 exports.raw !== true 时,Loader 也可以返回 Buffer 类型的结果
  6. return source;
  7. };
  8. // 通过 exports.raw 属性告诉 Webpack 该 Loader 是否需要二进制数据
  9. module.exports.raw = true;

每一个 loader 都可以用 String 或者 Buffer 的形式传递它的处理结果。Complier 将会把它们在 loader 之间相互转换。

使用缓存

在有些情况下,有些转换操作需要大量计算非常耗时,如果每次构建都重新执行重复的转换操作,构建将会变得非常缓慢。
为此,Webpack 会默认缓存所有 Loader 的处理结果,也就是说在需要被处理的文件或者其依赖的文件没有发生变化时,
是不会重新调用对应的 Loader 去执行转换操作的。
如果你想让 Webpack 不缓存该 Loader 的处理结果,可以这样:

  1. module.exports = function(source) {
  2. // 关闭该 Loader 的缓存功能
  3. this.cacheable(false);
  4. return source;
  5. };

注意:有依赖的 loader 无法使用缓存
**

编写准则

  • 简单易用。
  • 使用链式传递。
  • 模块化的输出。
  • 确保无状态。
  • 使用 loader utilities。
  • 记录 loader 的依赖。
  • 解析模块依赖关系。
  • 提取通用代码。
  • 避免绝对路径。
  • 使用 peer dependencies。

使用 loader utilities

充分利用 loader-utils 包。它提供了许多有用的工具,但最常用的一种工具是获取传递给 loader 的选项。schema-utils 包配合 loader-utils,用于保证 loader 选项,进行与 JSON Schema 结构一致的校验。这里有一个简单使用两者的例子:

  1. import { getOptions } from 'loader-utils';
  2. import validateOptions from 'schema-utils';
  3. const schema = {
  4. type: 'object',
  5. properties: {
  6. test: {
  7. type: 'string'
  8. }
  9. }
  10. }
  11. export default function(source) {
  12. const options = getOptions(this);
  13. validateOptions(schema, options, 'Example Loader');
  14. // 对资源应用一些转换……
  15. return `export default ${ JSON.stringify(source) }`;
  16. };