18. 自定义loader

100 阅读3分钟

Loader 的概念

webpack 本身只能识别 js 代码,loader 帮助 webpack 将不同类型的文件转换为 webpack 可以识别的模块。

loader 执行顺序

  1. 分类
  • pre: 前置 loader
  • normal: 普通 loader
  • inline: 内联 loader
  • post: 后置 loader
  1. 执行顺序
  • 4 类 loader 的执行优级为:pre > normal > inline > post 。
  • 相同优先级的 loader 执行顺序为:从右到左,从下到上。 例如:
// 此时loader执行顺序:loader3 - loader2 - loader1
module: {
  rules: [
    {
      test: /\.js$/,
      loader: "loader1",
    },
    {
      test: /\.js$/,
      loader: "loader2",
    },
    {
      test: /\.js$/,
      loader: "loader3",
    }
  ]
}
// 此时loader执行顺序:loader1 - loader2 - loader3
module: {
  rules: [
    {
      enforce: "pre",
      test: /\.js$/,
      loader: "loader1",
    },
    {
      // 没有enforce就是normal
      test: /\.js$/,
      loader: "loader2",
    },
    {
      enforce: "post",
      test: /\.js$/,
      loader: "loader3",
    }
  ]
}

开发 loader

1. 最简单的 loader

  1. loader 就是一个函数
  2. webpack 解析资源时会调用对应 loader
  3. loader 接收到文件内容作为参数,返回内容出去 webpack 配置:
module.exports = {
    entry: "/src/main.js",
    output: {
        // 所有文件的输出路径,开发模式没有输出
        path: undefined,
        // 入口文件打包输出文件名
        filename: "js/main.js"
    },
    module: {
        rules: [
           {
                test: /\.js$/,
                exclude: /node_modules/, // 排除node_modules代码不编译
                loader: "./loader/consoleLoader.js",
            }
        ]
    }
}
// loaders/consoleLoader.js
module.exports = function loader1(content) {
  console.log(content); // js 文件内容 打印 let a = 1
  return content;
};

入口文件 main.js:

let a = 1

2. loader 接受的参数

  • content 源文件的内容
  • map SourceMap 数据
  • meta 别的 loader 传来的数据

loader 分类

1. 同步 loader

module.exports = function (content, map, meta) {
  return content;
};

this.callback 方法则更灵活,因为它允许传递多个参数,而不仅仅是 content

module.exports = function (content, map, meta) {
    /**
     * 第一个参数:err 代表是否有错误,如果没有错误就是 null
     * 第二个参数:content 处理后的内容
     * 第三个参数:sourceMap 如果前一个loader传递了 sourceMap,还要把 sourceMap 传递下去
     * 第四个参数:给其他Loader传递的参数
     */
    this.callback(null, content, map, meta); 
    return;
};

2. 异步 loader

异步操作执行完之后,执行下一个操作。

module.exports = function (content, map, meta) {
  const callback = this.async();
  // 进行异步操作
  setTimeout(() => {
    callback(null, result, map, meta); // callback 方法调用之后,异步操作做完,执行下一个loader
  }, 1000);
};
  • 同步Loader中执行异步代码是有问题的

由于同步计算过于耗时,在 Node.js 这样的单线程环境下进行此操作并不是好的方案,我们建议尽可能地使你的 loader 异步化。但如果计算量很小,同步 loader 也是可以的。

3. Raw Loader

默认情况下,资源文件会被转化为 UTF-8 字符串,然后传给 loader。通过设置 raw 为 true,loader 可以接收原始的 Buffer。

module.exports = function (content) {
  // content是一个Buffer数据
  return content;
};
module.exports.raw = true; // 开启 Raw Loader

4. Pitching Loader

module.exports = function (content) {
  return content;
};
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  console.log("do somethings");
};

webpack 会先从左到右执行 loader 链中的每个 loader 上的 pitch 方法(如果有),然后再从右到左执行 loader 链中的每个 loader 上的普通 loader 方法。

image.png 在这个过程中如果任何 pitch 有返回值,则 loader 链被阻断。webpack 会跳过后面所有的的 pitch 和 loader,直接进入上一个 loader 。 image.png

loader API

方法名含义用法
this.async异步回调 loader。返回 this.callbackconst callback = this.async()
this.callback可以同步或者异步调用的并返回多个结果的函数this.callback(err, content, sourceMap?, meta?)
this.getOptions(schema)获取 loader 的 optionsthis.getOptions(schema)
this.emitFile产生一个文件this.emitFile(name, content, sourceMap)
this.utils.contextify返回一个相对路径this.utils.contextify(context, request)
this.utils.absolutify返回一个绝对路径this.utils.absolutify(context, request)

更多文档,请查阅 webpack 官方 loader api 文档

clean-log-loader

image.png

banner-loader

image.png image.png image.png

自定义babel-loader

image.png

image.png image.png

自定义file-loader

image.png image.png

自定义style-loader

image.png image.png image.png