加载中...
Webpack的loader和plugin的编写
发表于:2022-11-10 | 分类: 前端

loader 的链式调用和顺序执行

loader 就是一个 js 函数,它的作用就是接收源码,并返回新的源码。

如果是多个 loader 串行,则执行顺序是从右向左

module.exports = {
  rules: [
    {
      test: /\.less$/,
      use: ["style-loader", "css-loader", "less-loader"],
    },
  ],
};

如上,编译 less 文件的执行顺序为 less-loader -> css-loader -> style-loader

那么,为什么是从后往前的呢?

首先,函数的组合有两种情况,

  1. Unix 中的 pipline
  2. Compose

那么 webpack 中使用的就是 Compose, 它的原理如下:

compose =
  (f, g) =>
  (...args) =>
    f(g(...args));

我们可以通过如下的例子来验证

// a-loader.js
module.exports = function (source) {
  console.log("loader a");
  return source;
};

// b-loader.js
module.exports = function (source) {
  console.log("loader b");
  return source;
};

然后,将 loader 引入到 webpack 的配置中


module.exports = {
    entry: './src/index.js';
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    rules: [
        {
            test: /\.js$/,
            use: [
                path.resolve('./loaders/a-loader.js'),
                path.resolve('./loaders/b-loader.js'),
            ]
        }
    ]
}

借助loader-runner开发 loader
如下,开发一个 raw-loader

// run-loader.js
const { runLoaders } = require("loader-runner");
const fs = require("fs");
const path = require("path");

runLoaders(
  {
    resource: "./demo.txt",
    loaders: [
      {
        loader: path.resolve(__dirname, "./src/loaders/raw-loader"),
        options: {
          name: "test",
        },
      },
    ],
    readResource: fs.readFile.bind(fs),
  },
  function (err, result) {
    err ? console.log(err) : console.log(result);
  }
);

然后编写 loader

// raw-loader
module.exports = function (source) {
  const json = JSON.stringify(source)
    .replace(/\u2028/g, "\\u2028")
    .replace(/\u2029/g, "\\u2029");
  // return `export default ${json}`
  return this.callback(null, json); // 同步loader
};

loader 如何抛出错误

  • 直接通过 throw

  • 通过 this.callback 传递

this.callback(
    err: Error | null,
    content: string | Buffer,
    sourceMap?: SourceMap,
    meta?: any
)

loader 的异步处理
通过this.async来返回一个异步 loader

// raw-loader
module.exports = function (source) {
  const callback = this.async();
  const json = JSON.stringify(source)
    .replace(/\u2028/g, "\\u2028")
    .replace(/\u2029/g, "\\u2029");

  fs.readFile(path.join(__dirname, "./async.txt"), "utf-8", (err, result) => {
    callback(null, result);
  });
};

在 loader 中使用缓存

  • webpack 中默认开启 loader 缓存,可以使用 this.cacheable(false) 关掉缓存
  • 缓存条件: loader 在结果相同的输入下有确定的输出, 有依赖的 loader 无法缓存

自动合成雪碧图 loader

支持的语法:

background: url(‘a.png?**sprite’) ->
-> background: url(‘sprite.png’)
background: url(‘b.png?**sprite’) ->

插件的基本结构

class MyPlugin {
  // 1. 插件名称
  apply(compiler) {
    // 2. 插件上的apply方法,注入compiler
    compiler.hooks.done.tap("My Plugin", () => {
      // hooks
      console.log("...."); // 插件处理逻辑
    });
  }
}

module.exports = MyPlugin;
上一篇:
Webpack原理之HMR( Hot Module Replacement,可以翻译为「模块热更新」)
下一篇:
如何搭建一个前端开发环境(六):如何优化webpack?
本文目录
本文目录