从零实现一个 Webpack Plugin

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

内容简介:相比于 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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

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

Out of their Minds

Out of their Minds

Dennis Shasha、Cathy Lazere / Springer / 1998-07-02 / USD 16.00

This best-selling book is now available in an inexpensive softcover format. Imagine living during the Renaissance and being able to interview that eras greatest scientists about their inspirations, di......一起来看看 《Out of their Minds》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

URL 编码/解码
URL 编码/解码

URL 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具