loader

59 阅读2分钟

简介

其实也可以理解加加载器,比如你的ts,用tsloader去加载,里面的ts通过loader的ast变成js 他有几种类型,按照执行优先级:

  1. pre 前置loader
  2. normal 普通
  3. inline 内联
  4. post 后置相同优先级顺序为,从右到左,从上到下
// 此时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",
    },
  ],
},

写一个试试吧,

// loaders/loader1.js
module.exports = function loader1(content, map, meta) {
  console.log("hello loader");
  return content;
};

或者

module.exports = function (content, map, meta) {
  // 传递map,让source-map不中断
  // 传递meta,让下一个loader接收到其他参数
  this.callback(null, content, map, meta);
  return; // 当调用 callback() 函数时,总是返回 undefined
};

或者

module.exports = function (content, map, meta) {
  const callback = this.async();
  // 进行异步操作
  setTimeout(() => {
    callback(null, result, map, meta);
  }, 1000);
};

简简单单一个loader

  • content 源文件的内容
  • map SourceMap 数据
  • meta 数据,可以是任何内容### 使用:

row-loader

这时候接受的是原始buffer,不然默认是utf-8字符串

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

pitching loader

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

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

在这个过程中如果任何 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获取 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)

简单实现

function myRawLoader(source) {
  // 提取给定的 loader 选项,
  // 从 webpack 5 开始,this.getOptions 可以获取到 loader 上下文对象。它用来替代来自 loader-utils 中的 getOptions 方法。
  const { esModule } = this.getOptions();
  console.log('esModule:', esModule);

  // 这里一定要返回字符串或者 buffer
  if (!esModule) {
    return `module.exports = ${JSON.stringify(source)}`;
  }
  return `export default ${JSON.stringify(source)}`;

module.exports = myRawLoader;

简单用

const path = require('path');

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: path.resolve('./myRawLoader.js'),
            options: {
              /* ... */
            },
          },
        ],
      },
    ],
  },
  // 多个loaders情况
  resolveLoader: {
    modules: ['node_modules', path.resolve(__dirname, 'loaders')],
  },
};