Webpack - 自定义 Loader

Loader Interface

loader 本质上是 JS 函数。loader runner 会调用此函数。

js 复制代码
function webpackLoader(content, map, meta) {
  // ...
}

参数说明:

  • content: string | Buffer

    • 资源源文件内容 或者 是上一个 loader 处理的结果;
  • map: object

    • SourceMap 数据
  • meta: any

    • meta 数据,可以是任何内容(可以是其他 loader 传递过来的参数)

同步 Loader

通过 returnthis.callback 都可以同步地返回转换后的 content。

函数中的 this 作为上下文会被 webpack 填充。

js 复制代码
// 通过 return 同步返回转换后的 content
module.exports = function (content, map, meta) {
  return someSyncOperation(content);
};

// this.callback 可以传递更多参数
module.exports = function (content, map, meta) {
  this.callback(null, someSyncOperation(content), map, meta);
  return; // 当调用 callback() 函数时,总是返回 undefined
};

异步 Loader

通过 this.async 获取 callback 函数。

js 复制代码
module.exports = function (content, map, meta) {
  var callback = this.async();
  someAsyncOperation(content, function (err, result) {
    if (err) return callback(err);
    callback(null, result, map, meta);
  });
};

由于同步计算过于耗时,建议尽可能地使你的 loader 异步化。但如果计算量很小,同步 loader 也是可以的。

"Raw" Loader

默认情况下,资源文件会被转化为 UTF-8 字符串,然后传给 loader。通过设置 raw 为 true,loader 可以接收原始的 Buffer。每一个 loader 都可以用 String 或者 Buffer 的形式传递它的处理结果。complier 将会把它们在 loader 之间相互转换。

js 复制代码
// "Raw" Loader 常用来处理 图片,字体图标等媒体资源
module.exports = function (content) {
  return someSyncOperation(content);
};

module.exports.raw = true;

Pitching Loader

loader 总是 从右到左被调用。在实际(从右到左)执行 loader 之前,会先 从左到右 调用 loader 上的 pitch 方法。

js 复制代码
// 传递给 pitch 方法的 data,在执行阶段也会暴露在 this.data 之下
module.exports = function (content) {
  return someSyncOperation(content, this.data.value);
};

module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  data.value = 42;
};
rust 复制代码
比如处理 scss 样式文件时,webpack 有以下配置:

use: ['style-loader', 'css-loader', 'sass-loader']

执行顺序:
-> style-loader pitch 
-> css-loader pitch
-> sass-loader pitch 
-> sass-loader 
-> css-loader 
-> style-loader

如果 css-loader 的 pitch 方法 return 了一个结果,将跳过剩余 loader

顺序如下:
-> style-loader pitch 
-> css-loader pitch
-> style-loader

The Loader Context

webpack.docschina.org/api/loaders...

loader context 表示在 loader 内使用 this 可以访问的一些方法或属性。

示例

js 复制代码
// 清除 console.log 的 loader
module.exports = function (source, map, meta) {
  const newSource = source.replace(/console.log(.+?)(;\n)?/g, "");
  this.callback(null, newSource, map, meta);
};

// 简易 style-loader
module.exports.pitch = function (remainingRequest) {
  // 将绝对路径转换为相对路径后,直接引用
  const relativePath = remainingRequest
    .split("!")
    .map((absolutePath) => {
      // 将每个绝对路径转换为相对路径
      return this.utils.contextify(this.context, absolutePath);
    })
    .join("!");
  const newSource = `
    import style from "!!${relativePath}";
    const _style = document.createElement("style");
    _style.innerHTML = style;
    document.head.appendChild(_style);
  `;
  return newSource;
};

// 简易 file-loader
const loaderUtils = require("loader-utils");
module.exports = function (source) {
  // 根据文件内容生成 hash
  const interpolatedName = loaderUtils.interpolateName(
    this,
    "[hash].[ext][query]",
    {
      content: source,
    }
  );
  // 将文件输出
  this.emitFile(interpolatedName, source);
  return `module.exports = "${interpolatedName}"`;
};
module.exports.raw = true;

// 简易 babel-loader 
const babel = require("@babel/core");
const schema = require("./schema.json");

module.exports = function (source) {
  // 异步loader
  const callback = this.async();
  // schema 对参数进行校验
  const options = this.getOptions(schema);

  babel.transform(source, options, function (err, result) {
    if (err) callback(err);
    callback(null, result.code);
  });
};

// schema.json
{
  "type": "object",
  "properties": {
    "presets": {
      "type": "array"
    }
  },
  "additionalProperties": true
}
相关推荐
会豪1 小时前
Electron-Vite (一)快速构建桌面应用
前端
中微子1 小时前
React 执行阶段与渲染机制详解(基于 React 18+ 官方文档)
前端
唐某人丶1 小时前
教你如何用 JS 实现 Agent 系统(2)—— 开发 ReAct 版本的“深度搜索”
前端·人工智能·aigc
中微子1 小时前
深入剖析 useState产生的 setState的完整执行流程
前端
遂心_2 小时前
JavaScript 函数参数传递机制:一道经典面试题解析
前端·javascript
小徐_23332 小时前
uni-app vue3 也能使用 Echarts?Wot Starter 是这样做的!
前端·uni-app·echarts
RoyLin2 小时前
TypeScript设计模式:适配器模式
前端·后端·node.js
遂心_2 小时前
深入理解 React Hook:useEffect 完全指南
前端·javascript·react.js
Moonbit2 小时前
MoonBit 正式加入 WebAssembly Component Model 官方文档 !
前端·后端·编程语言
龙在天2 小时前
ts中的函数重载
前端