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
}
相关推荐
优雅永不过时·23 分钟前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
神夜大侠3 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱3 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号3 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72933 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲4 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
王解4 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里4 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱4 小时前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
PyAIGCMaster4 小时前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python