从零实现一个 Webpack Plugin

栏目: JavaScript · 发布时间: 6年前

内容简介:相比于 loaders,plugin 更加的灵活,因为它能够接触到 webpack 编译器和编译核心。这就使得 plugin 可以通过一些 hook 函数来拦截 webpack 的执行,甚至你可以运行一个子编译器和 loader 串联,像示例代码:以
Plugins expose the full potential of the webpack engine to third-party developers. ----------- Webpack

相比于 loaders,plugin 更加的灵活,因为它能够接触到 webpack 编译器和编译核心。这就使得 plugin 可以通过一些 hook 函数来拦截 webpack 的执行,甚至你可以运行一个子编译器和 loader 串联,像 MiniCssExtractPlugin 就是这么做的。

示例代码: link

webpack plugin 基本结构

html-webpack-plugin 为例,它的使用如下

plugins: [
    new HtmlWebpackPlugin({
        ...
    }),
],
复制代码

不难看出,webpack plugin 的基本形式一个构造函数 new function() ,同时为了能够获得 compiler,就需要 plugin 对外暴露一个接口(为 apply 函数)。所以,它的基本结构如下:

  • 一个命名的 JavaScript 函数对象;
  • 在其 prototype 上,定义一个 apply 方法。

JavaScript 的实现这种形式的方法有很多,本文采用 class 来实现,具体如下

module.exports = class DemoPlugin {
    apply() {
        console.log('applying')
    }
}
复制代码

配置开发环境

为了能够运行这个 plugin,我们需要创建一个环境

mkdir webpack-demo-plugin
cd webpack-demo-plugin
npm init
复制代码

webpack.plugin.js

const path = require("path");

const PATHS = {
  lib: path.join(__dirname, "index.js"),
  build: path.join(__dirname, "build"),
};

module.exports = {
  entry: {
    lib: PATHS.lib,
  },
  output: {
    path: PATHS.build,
    filename: "[name].js",
  },
};
复制代码

index.js

console.log("hello world")
复制代码

同时向 package.json 中添加

"scripts": {
    "build:plugin": "webpack --config webpack.plugin.js --mode production",
    ...
  }
复制代码

实现 webpack demo

创建 plugins/demo-plugin.js 文件,内容为之前的 webpack plugin demo,并将其引入到 webpack.plugin.js 内。

webpack.plugin.js

const DemoPlugin = require("./plugins/demo-plugin.js");

module.exports = {
  ...
  // 引入 plugin
  plugins: [new DemoPlugin()],
};
复制代码

尝试运行下 npm run build:plugin ,终端上打印出

applying
Hash: 98c8997160aa995a58a4
Version: webpack 4.12.0
Time: 93ms
Built at: 2019-04-29 14:34:31
 Asset       Size  Chunks             Chunk Names
lib.js  956 bytes       0  [emitted]  lib
[0] ./index.js 26 bytes {0} [built]
复制代码

惊奇的发现 applying,说明插件已经成功运行。

传递参数

在应用一个 plugin 时,有时需要根据传递 Options 来告诉 plugin 具体应该做什么。当 new DemoPlugin() 时候会触发 class DemoPluginconstructor 所以

plugins/demo-plugin.js

module.exports = class DemoPlugin {
    constructor(options) {
        this.options = options
    }
    apply() {
        console.log('apply', this.options)
    }
}
复制代码

同时,还需要修改 webpack.plugin.js 来传递对应参数

module.exports = {
    ...
    plugins: [new DemoPlugin({ name: 'demo' })],
}
复制代码

运行 npm run build:plugin ,可以发现 apply { name: 'demo' } 。这里介绍一个常用插件schema-utils 能够用来校验 Options。

理解 webpack 的 compiler 和 compilation

在之前的 webpack plugin 基本结构中介绍, apply 函数能够用来访问 webpack 的核心。具体的做法是, apply 函数的参数为 compiler

plugins/demo-plugin.js

module.exports = class DemoPlugin {
    constructor(options) {
        this.options = options
    }
    apply(compiler) {
        console.log(compiler)
    }
}
复制代码

再次运行 npm run build:plugin ,会发现终端上打印出了 compiler 的全部信息,其中 hooks 字段占了绝大部分。

对照着官方文档,你会发现每一个 hook 对应一个特定的阶段。 例如,emit 实践是在向输出目录发送资源之前执行。这样就可以通过监听 hook 来实现控制编译过程。

plugins/demo-plugin.js

module.exports = class DemoPlugin {
    constructor(options) {
        this.options = options
    }
    apply(compiler) {
        compiler.plugin('emit', (compilation, next) => {
            console.log(compilation)

            next()
        })
    }
}
复制代码

不要忘记调用 next,否则 webpack 将不会继续打包。

运行 npm run build:plugin 会显示出比以前更多的信息,因为编译对象包含webpack 遍历的整个依赖关系图。 你可以访问与此相关的所有内容,包括 entries, chunks, modules, assets等。

通过 Compilation 写入文件

可以通过 compilationassets 对象来编写新的文件,或是修改已经创建的文件。为了更好地写入文件,我们引入一个 npm 包

npm install webpack-sources --save-dev
复制代码

plugins/demo-plugin.js

const { RawSource } = require("webpack-sources");

module.exports = class DemoPlugin {
    constructor(options) {
        this.options = options
    }
    apply(compiler) {
        const { name } = this.options;

        compiler.plugin('emit', (compilation, next) => {
            compilation.assets[name] = new RawSource("demo");

            next()
        })
    }
}
复制代码

在终端运行 npm run build:plugin

Hash: 98c8997160aa995a58a4
Version: webpack 4.12.0
Time: 95ms
Built at: 2019-04-29 16:08:52
 Asset       Size  Chunks             Chunk Names
lib.js  956 bytes       0  [emitted]  lib
  demo    4 bytes          [emitted]
[0] ./index.js 26 bytes {0} [built]
复制代码

在 Asset 那里一列内,出现了我们自定的 demo 文件。

更多的钩子函数,请见 the official compilation reference

管理 Warnings 和 Errors

做一个实验,如果你在 apply 函数内插入 throw new Error("Message") ,会发生什么,终端会打印出 Unhandled rejection Error: Message 。然后 webpack 中断执行。

为了不影响 webpack 的执行,要在编译期间向用户发出警告或错误消息,则应使用 compilation.warningscompilation.errors

compilation.warnings.push("warning");
compilation.errors.push("error");
复制代码

以上所述就是小编给大家介绍的《从零实现一个 Webpack Plugin》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

圈圈教你玩USB

圈圈教你玩USB

刘荣 / 2013-4 / 59.00元

通过U盘、USB鼠标、15SB键盘、USBMIDI键盘、USB转串口、自定义的USBHID设备和自定义的USB设备等几个具体的USB例子,一步步讲解USB设备及驱动程序和应用程序开发的详细过程和步骤。第9和10章介绍USBWDM驱动开发,并给出一个简单的USB驱动和USB上层过滤驱动的实例。第2版中新增4章内容,包括USB触摸屏设备、移植到AVR单片机和ARM微控制器上以及更多的USB设备的实现。......一起来看看 《圈圈教你玩USB》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具