继上回介绍了如何开发webpack loader 之后。趁热打铁,来继续看下webpack另一个核心组成:plugin。

下面也和loader一样,让我们一起从基本的官方文档着手看起。

loader和plugin的差别

  • loader : 顾名思义,某种类型资源文件的加载器,作用于某种类型的文件上。webpack本身也是不能直接打包这些非js文件的,需要一个转化器即loader。 loader本身是单一,简单的,不能将多个功能放在一个loader里。
  • plugin比loaders更加先进一点,你可以扩展webpack的功能来满足自己的需要,换句话说,loader不能满足的时候,就需要plugin了。

如何开发一个plugin

插件将webpack引擎所有的能力暴露给第三方开发者。通过阶梯式的build回调,开发者可以在webpack编译过程中加入自己的行为。开发插件比loaders更加先进一点,因为你需要理解webpack一些底层构成来添加钩子回调。准备好读一些源码吧。

开发一个插件

一个webpack的插件由以下几方面组成:

  • 一个非匿名的js函数
  • 在它的原型对象上定义apply方法
  • 指明挂载自身的webpack钩子事件
  • 操作webpack内部情况的特定数据
  • 方法完成时唤起webpack提供的回调
// A named JavaScript function.
function MyExampleWebpackPlugin() {
//
};
// Defines `apply` method in it's prototype.
MyExampleWebpackPlugin.prototype.apply = function(compiler) {
// Specifies webpack's event hook to attach itself.
compiler.plugin('webpacksEventHook', function(compilation /* Manipulates webpack internal instance specific data. */, callback) {
console.log("This is an example plugin!!!");
// Invokes webpack provided callback after functionality is complete.
callback();
});
};

编译器和编译

开发插件过程中最重要的两个对象就是compiler 和compilation。理解他们的职责是扩展webpack功能最重要的第一步

编译器对象就是webpack完整的配置环境。该对象一经webpack开始执行就创建,并且通过所有可操作的设置项来设置,例如options,loaders,和plugins。当在webpack环境中应用一个插件时,该插件将会接受到一个指向该编译器的引用。使用该编译器来访问主要的webpack环境。

compilation对象是一个单独的关于版本资源的创建。当执行webpack 开发中间件时,当一个文件的更改被检测到就会创建一个新的compilation对象,因此产生了一些可被编译的资源。一个compilation展现了一些信息关于当前模块资源状态、编译资源、改变的文件、监视的依赖等信息。同样提供了很多关键的回调,当插件扩展自定义行为时

这两个组件是webpack 插件必需的组成部分(特别是compilation),所以开发者如果熟悉下面这些源文件将会获益不小。

插件的基本结构

插件是在原型中带有一个apply方法的实例化对象,当安装插件的时候,这个apply方法就会被webpack调用一次。apply方法提供一个指向当前活动的webpack compiler的引用,该引用允许访问compiler的回调。一个简单的插件结构如下:

function HelloWorldPlugin(options) {
// Setup the plugin instance with options...
} HelloWorldPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', function() {
console.log('Hello World!');
});
}; module.exports = HelloWorldPlugin;

然后安装一个插件,仅仅需要在你的 webpack config 中plugins对应的数组中,增加一个插件的实例即可

var HelloWorldPlugin = require('hello-world');

var webpackConfig = {
// ... config settings here ...
plugins: [
new HelloWorldPlugin({options: true})
]
};

访问编译

通过使用编译器对象,你可能会绑定提供指向每个新的compilation应用的回调。这些compilations提供了编译过程中很多步骤的回调函数。

function HelloCompilationPlugin(options) {}

HelloCompilationPlugin.prototype.apply = function(compiler) {

  // Setup callback for accessing a compilation:
compiler.plugin("compilation", function(compilation) { // Now setup callbacks for accessing compilation steps:
compilation.plugin("optimize", function() {
console.log("Assets are being optimized.");
});
});
}; module.exports = HelloCompilationPlugin;

如果想了解更多关于在编译器、编译中哪些回调是可用的和其他一些更重要的对象,轻戳plugin文档

异步插件

一些编译插件步骤是异步的并且提供了一个当你的插件结束编译时必须调用的回调方法

function HelloAsyncPlugin(options) {}

HelloAsyncPlugin.prototype.apply = function(compiler) {
compiler.plugin("emit", function(compilation, callback) { // Do something async...
setTimeout(function() {
console.log("Done with async work...");
callback();
}, 1000); });
}; module.exports = HelloAsyncPlugin;

示例

一旦我们打开了webpack编译器和每个单独编译的大门,我们可以使用引擎做的事情是无限可能的。我们可以重新格式化存在的文件、创建派生文件、完全伪造一个新文件

让我们写个简单的示例插件,目的是生成一个新的名字为filelist.md的文件。内容如下:列出构建过程中所有的生成文件。这个插件大概如下:

function FileListPlugin(options) {}

FileListPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
// Create a header string for the generated file:
var filelist = 'In this build:\n\n'; // Loop through all compiled assets,
// adding a new line item for each filename.
for (var filename in compilation.assets) {
filelist += ('- '+ filename +'\n');
} // Insert this list into the webpack build as a new file asset:
compilation.assets['filelist.md'] = {
source: function() {
return filelist;
},
size: function() {
return filelist.length;
}
}; callback();
});
}; module.exports = FileListPlugin;

不同类型的插件

插件可以依据其注册的事件来分成不同的类型,每个事件钩子决定了在触发时如何调用该插件。

同步类型

这种类型的实例使用如下方式来调用插件

applyPlugins(name: string, args: any...)

applyPluginsBailResult(name: string, args: any...)

这意味着每一个插件的回调将伴随特定参数args依次被调用。对插件而言这是最简单的格式。很多有用的事件例如"compile", "this-compilation",是期望插件同步执行的。

流式类型

waterfall Plugins 通过下面的方式调用

applyPluginsWaterfall(name: string, init: any, args: any...)

异步类型

当所有的插件被使用下面的方法异步调用的时候,即为异步插件

applyPluginsAsync(name: string, args: any..., callback: (err?: Error) -> void)

插件控制方法被调用,参数是所有的args和带有这种标志(err?: Error) -> void的回调。handler方法按照注册回调在所有handlers被调用之后的顺序来调用。对于"emit", "run"事件来说这是很常用的模式。

异步流

这种插件将按照流失方式来被异步使用

applyPluginsAsyncWaterfall(name: string, init: any, callback: (err: Error, result: any) -> void)

这种插件的handler被调用时,参数是当前value和带有这种标志(err?: Error) -> void的回调。当被调用时,nextValue是下一个handler的当前值。第一个handler的当前值是init。所有的handler被调用之后,最后一个值将会被赋给回调。如果有的handler传递了一个err的值,回调将会接受err,并且不会有其他handler被第阿勇。这种插件模式使用与于"before-resolve" and "after-resolve"之类的事件。

异步系列

这种和异步插件很相似,不同在于如果有点插件注册失败,将不会调用任何插件

applyPluginsAsyncSeries(name: string, args: any..., callback: (err: Error, result: any) -> void)

结束语

至此,如何开发一个基本的webpack plugin 我相信大家已经知道了,如果还不太清楚的话,可以移步w-loader查看。

另外,对于我这种英语渣渣来说,翻译起来确实难度蛮大的。此处抛砖引玉,希望大家共同探讨学习。

此文为原创文章,转载请注明出处!

如何开发webpack plugin的更多相关文章

  1. 简单webpack plugin 开发

    重要是学习下怎么开发webpack plugin,同时记录下 插件模型 webpack 是一个插件,可以是javascript class ,或者具名 class 定义apply 方法 指定一个绑定到 ...

  2. YYDS: Webpack Plugin开发

    目录 导读 一.cdn常规使用 二.开发一个webpack plugin 三.cdn优化插件实现 1.创建一个具名 JavaScript 函数(使用ES6的class实现) 2.在它的原型上定义 ap ...

  3. 揭秘webpack plugin

    前言 Plugin(插件) 是 webpack 生态的的一个关键部分.它为社区提供了一种强大的方法来扩展 webpack 和开发 webpack 的编译过程.这篇文章将尝试探索 webpack plu ...

  4. vue(9)—— 组件化开发 - webpack(3)

    前面两个终于把webpack相关配置解析完了.现在终于进入vue的开发了 vue组件化开发预热 前期准备 创建如下项目: app.js: footer.js: main.js: webpack.con ...

  5. vue(8)—— 组件化开发 - webpack(2)

    webpack的常用loder和插件 loder和插件是什么,现在暂且不表,看到后面你就懂了 引入css问题 直接用link标签导入css 在前面的 vue(7)—— 组件化开发 — webpack( ...

  6. Webpack Plugin

    [Webpack Plugin] Since Loaders only execute transforms on a per-file basis, plugins are most commonl ...

  7. 案例实战之如何写一个webpack plugin

    案例实战之如何写一个webpack plugin 1.写一个生成打包文件目录的file.md文件 // 生成一个目录项目目录的文件夹 class FileListPlugin { constructo ...

  8. 开发 IDEA Plugin 引入探针,基于字节码插桩获取执行SQL

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 片面了! 一月三舟,托尔斯泰说:"多么伟大的作家,也不过就是在书写自己的片 ...

  9. 如何开发webpack loader

    关于webpack 作为近段时间风头正盛的打包工具,webpack基本占领了前端圈.相信你都不好意思说不知道webpack. 有兴趣的同学可以参考下我很早之前的webpack简介 . 确实webpac ...

随机推荐

  1. 微信小程序swiper标签的测试

    swiper属性(具体看官方文档): 一:testswiper.wxml的代码如下.testswiper.js自动生成示例代码. //testswiper.wxml <view > < ...

  2. java继承中的初始化顺序

    初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类 ...

  3. 201521123015 《Java程序设计》第6周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 2. 书面作业 1.clone方法 1.1 Object对 ...

  4. 201521123004《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 本周主要内容为: 继承:extends 抽取共同特征(行为与属性) 复用代码 继承时子类将获 ...

  5. Java课程设计 购物车系统(个人博客)

    1. 团队课程设计博客链接 课程设计 2. 个人负责模块或任务说明 编写ShoppingCart类,连接数据库 编写updateCart类,从数据库中获取商品信息,获取指定编号的商品信息 编写User ...

  6. 201521123029《Java程序设计》第十三周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1.网络基础 1.1 比较ping www.baidu.com与ping cec.jmu. ...

  7. Activiti-03-query api

    Query API 有两种方式从引擎中查询数据, 查询 API 和本地查询. API方式:   List<Task> tasks = taskService.createTaskQuery ...

  8. hibernate中Query的list和iterator区别(续)

    打开cache后query的list和iterator方法区别 将query 的cache打开的话,缓存的是query本身,以hql 生成的 sql ,再加上参数,分页等信息做为key值,而不是que ...

  9. [js高手之路] es6系列教程 - Set详解与抽奖程序应用实战

    我们还是从一些现有的需求和问题出发,为什么会有set,他的存在是为了解决什么问题? 我们看一个这样的例子,为一个对象添加键值对 var obj = Object.create( null ); obj ...

  10. C#中的两把双刃剑:抽象类和接口

    问题出现: 这也是我在学习抽象类和接口的时候遇到的问题,从我归纳的这三个问题,不难看出这也许是我们大多数程序员遇到问题的三个阶段, 第一阶段(基础概念):就象问题1一样,这部分人首先需要扫清基础概念的 ...