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
}
相关推荐
子兮曰16 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰16 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万16 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝16 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋17 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁18 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王952719 小时前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大19 小时前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师20 小时前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学20 小时前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端