[转] webpack之plugin内部运行机制
简介
webpack作为当前最为流行的模块打包工具,几乎所有的主流前端开发框架(React、Vue等)都会将其作为默认的模块加载和打包工具。通过简单的配置项,使用各种相关的loader和plugin,我们就可以实现自动的模块依赖分析并打包,从而大大降低了前端项目的开发复杂度,明显提高了前端项目的开发效率。
其中,plugin是webpack核心支柱功能,通过plugin(插件)webpack可以实现loader所不能完成的复杂功能,使用plugin丰富的自定义API以及生命周期事件,可以控制webpack编译流程的每个环节,实现对webpack的自定义功能扩展。
webpack基础概念
entry
entry
表示webpack编译的入口文件,通常由html通过script标签引入。
entry的配置参见官方文档说明。
loader
loader
用于对模块的源代码进行转换。其类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL,或者将CSS文件、markdown等非js文件转换为js文件并require
进来!
loader特性
loader使用方法
- webpack.config.js
1
2
3
4
5
6
7
8
|
module.exports = {
module: {
rules: [
{test: /\.css$/, use: 'css-loader'},
{test: /\.ts$/, use: 'ts-loader'}
]
}
};
|
require
语句中使用
1
|
require('style-loader!css-loader?modules!./styles.css');
|
module
Module
是webpack的中的核心实体,要加载的一切和所有的依赖都是Module,总之一切都是Module。
- webpack支持的模块类型:
- ES2015模块,使用
import
来加载 - CommonJS模块,使用
require()
加载。例如:Node.js模块 - AMD模块,使用
require
加载。例如:require.js支持的异步加载模块 - css/sass/less 模块文件
- image等其它非js模块文件
- ES2015模块,使用
webpack 通过 loader
可以支持各种语言和预处理器编写模块。loader 描述了webpack 如何处理非js模块,可将这些非js模块进行转换,然后可以通过普通的js模块加载方式来使用。
chunk
chunk
是webpack使用code splitting后的产物,也就是按需加载的分块,装载了不同的 module
。
module 和 chunk 的关系图:
webpack将chunk分为三种类型
entry chunk
入口代码块包含了 webpack 运行时需要的一些函数,如webpackJsonp
,__webpack_require__
等以及依赖的一系列模块normal chunk
普通代码块没有包含运行时需要的代码,主要指代那些应用运行时动态加载
的模块,其结构有加载方式决定,如基于异步的方式可能会包含webpackJsonp
的调用。initial chunk
initial chunk
本质上还是normal chunk
,不过其会在应用初始化时完成加载,往往这个类型的chunk由CommonsChunkPlugin
生成。与入口代码块对应的一个概念是入口模块(
module 0),如果入口代码块中包含了入口模块 webpack 会立即执行这个模块,否则会等待包含入口模块的代码块,包含入口模块的代码块其实就是initial chunk
。
code splitting
利用webpack提供的code splitting功能可生成不同类型的chunk,具体请参照另外一篇文章《基于webpack实现react组件的按需加载》
如何编写一个webpack plugin
plugin创建
plugin是一个具有 apply
方法的 js对象。 apply方法会被 webpack的 compiler
(编译器)对象调用,并且 compiler
对象可在整个 compilation
(编译)生命周期内访问。
webpack插件的组成:
- 一个JavaScript
函数
或者class
(ES6语法)。 - 在它的原型上定义一个
apply
方法。 - 指定挂载的webpack
事件
钩子。 - 处理webpack内部实例的特定数据。
- 功能完成后调用webpack提供的
回调
。
这里以我们常用的 UglifyJsPlugin
为分析示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
class UglifyJsPlugin {
apply(compiler) {
const options = this.options;
options.test = options.test || /\.js($|\?)/i;
......
//绑定compilation事件
compiler.plugin("compilation", (compilation) => {
if(options.sourceMap) {
compilation.plugin("build-module", (module) => {
// to get detailed location info about errors
module.useSourceMap = true;
});
}
//绑定optimize-chunk-assets事件
compilation.plugin("optimize-chunk-assets", (chunks, callback) => {
const files = [];
chunks.forEach((chunk) => files.push.apply(files, chunk.files));
......
callback();
});
});
}
}
module.exports = UglifyJsPlugin;
|
plugin用法
由于plugin可以携带参数/选项,你必须在wepback配置中,向plugins属性传入 new实例。这里介绍两种常用的使用plugin的方式。
webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
module: {
loaders: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(), //访问内置的插件
new HtmlWebpackPlugin({template: './src/index.html'}) //访问第三方插件
]
};
|
Node.js
目前webpack官方强烈建议在配置文件中使用plugins属性配置项来使用各种plugin,而在Node.js中通过
compiler.apply
来使用plugin这种方式并不推荐。
1
2
3
4
5
6
7
8
9
|
const webpack = require('webpack'); //运行时(runtime)访问 webpack
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
compiler.apply(new webpack.optimize.UglifyJsPlugin());
compiler.run(function(err, stats) {
// ...
});
|
webpack整体运行流程图
TODO
Tapable
webpack整体是一个插件架构,所有的功能都以插件的方式集成在构建流程中,通过发布订阅事件来触发各个插件执行。webpack核心使用Tapable 来实现插件(plugins)的binding(绑定)和applying(应用)。
tapable介绍
tapable是webpack官方开发维护的一个小型库,能够让我们为javascript模块添加并应用插件。 它可以被其它模块继承或混合。它类似于NodeJS的 EventEmitter
类,专注于自定义事件
的发射
和操作
。 除此之外, Tapable 允许你通过回调函数的参数访问事件的生产者。
tapable核心函数
compiler(编译器)和compilation(编译)
在webpack插件开发中最重要的两个核心概念就是 compiler
和 compilation
。理解他们是扩展webpack功能的关键。
compiler
webpack官方对compiler的解释如下:
The compiler object represents the fully configured webpack environment. This object is built once upon starting webpack, and is configured with all operational settings including options, loaders, and plugins. When applying a plugin to the webpack environment, the plugin will receive a reference to this compiler. Use the compiler to access the main webpack environment
compiler对象代表的是配置完备的Webpack环境。 compiler对象只在Webpack启动时构建一次,由Webpack组合所有的配置项构建生成。
功能核心
Compiler
继承自前面我们介绍的Tapable
类,其混合了 Tapable 类以吸收其功能来注册和调用自身的插件。 大多数面向用户的插件,都是首先在 Compiler 上注册的。
compiler事件钩子(Event Hooks)
webpack官方列出了Compiler的所有事件钩子,点击查看
我们这里只介绍一些关键性的事件,大致按照webpack流程的执行顺序:
compiler源码
compilation
webpack官方解释如下:
A compilation object represents a single build of versioned assets. While running webpack development middleware, a new compilation will be created each time a file change is detected, thus generating a new set of compiled assets. A compilation surfaces information about the present state of module resources, compiled assets, changed files, and watched dependencies. The compilation also provides many callback points at which a plugin may choose to perform custom actions.
compilation
对象代表了一次单一的版本构建和生成资源。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源。一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。编译对象也提供了很多关键点事件回调
供插件做自定义处理时选择使用。
功能核心
Compilation
对象负责组织整个编译过程,包含了每个构建环节所对应的方法。前面提到的Compiler
(编译器)的run
方法中调用compiler
方法开始编译,在编译过程中创建了一个Compilation
对象。
Compilation事件钩子(Event Hooks)
- 获取Compilation对象
1
2
3
4
|
compiler.plugin("compilation", function(compilation) {
//主要的编译实例
//随后所有的方法都从 compilation.plugin 上得来
});
|
- Compilation(编译)事件钩子
- Compilation绑定事件示例:
1
2
3
4
5
6
7
8
9
10
|
//这里一般只有一个块,除非你在配置中指定了多个入口
compilation.plugin('optimize-chunks', function(chunks) {
//这里一般只有一个块,除非你在配置中指定了多个入口
chunks.forEach(function (chunk) {
//块含有模块的循环引用
chunk.modules.forEach(function (module){
//module.loaders, module.rawRequest, module.dependencies 等。
});
});
});
|
compilation源码
二者关系
compiler
对象代表的是不变的webpack环境,是针对webpack的compilation
对象针对的是随时可变的项目文件,只要文件有改动,compilation就会被重新创建。
Module
module 是 webpack 构建的核心实体,也是所有 module 的 父类,它有几种不同子类:NormalModule
, MultiModule
, ContextModule
, DelegatedModule
等。但这些核心实体都是在构建中都会去调用对应方法,也就是build()
。
build方法核心源代码点击查看
Template
Template
是用来生成结果代码的。入口是compilation
的createChunkAssets
方法。
1
2
3
4
5
6
|
//如果是入口,则使用MainTemplate生成结果,否则使用ChunkTemplate.
if(chunk.entry) {
source = this.mainTemplate.render(this.hash, chunk, this.moduleTemplate, this.dependencyTemplates);
} else {
source = this.chunkTemplate.render(chunk, this.moduleTemplate, this.dependencyTemplates);
}
|
Template子类
- MainTemplate.js 用于生成项目入口文件
- ChunkTemplate.js 用于生成异步加载的js代码
- ModuleTemplate.js 用于生成某个模块的代码
- HotUpdateChunkTemplate.js 热更新chunk
Template源代码
参考资料
[转] webpack之plugin内部运行机制的更多相关文章
- Windows程序内部运行机制 转自http://www.cnblogs.com/zhili/p/WinMain.html
一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序 ...
- 关于js内部运行机制的一本好书
读<单页Web应用一书>,第二章讲了js内部运行机制,感觉棒极了.之前读<你不知道的js>,看的云里雾里,似懂非懂.没想到单页Web一书将此内容讲的如此通俗易懂,好多困惑已久的 ...
- 深入浅出话VC++(1)——Windows程序内部运行机制
一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序 ...
- 深入理解ASP.NET的内部运行机制(转)
WebForms和WebServices作为.NET平台构建Web程序的两大利器,以其开发简单.易于部署的特点得到了广泛的应用,但殊不知微软公司在背后为我们做了大量的基础性工作,以至于我们开发人员只需 ...
- IIS 内部运行机制及Asp.Net执行过程详解
一直以来对一个Asp.net页面穿过IIS后就返回给浏览器一个HTML页面感觉很是神奇.虽然做技术这么长时间了,也曾经大致了解过一点来龙去脉,但是如果你真的问起我比较详细的过程,我还真的回答不上来,好 ...
- windows程序内部运行机制
Windows程序内部运行机制 2007-10-21 19:52 1010人阅读 评论(0) 收藏 举报 windowsvc++applicationcallbackwinapistructure W ...
- IIS 内部运行机制
ASP.NET是一个非常强大的构建Web应用的平台,它提供了极大的灵活性和能力以致于可以用它来构建所有类型的Web应用. 绝大多数的人只熟悉高层的框架如: WebForms 和 WebServices ...
- 从template到DOM(Vue.js源码角度看内部运行机制)
写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...
- 第1章 Windows程序内部运行机制
参考: https://blog.csdn.net/u014162133/article/details/46573873 1.Windows API与Win32 SDK 操作系统提供了各种方便开发W ...
随机推荐
- Delphi 三层框架 DataSnap 的服务器端设置
elphi 三层框架 DataSnap 的服务器端设置: DataSnap 框架有三个模块:DataSnap Server,Server Module,DataSnap Client Module. ...
- 获取图片的EXIF信息
对于专业的摄影师来说,Exif信息是很重要的信息,也包含了非常多的东西 1.EXIF EXIF(Exchangeable Image File)是“可交换图像文件”的缩写,当中包含了专门为数码相机的照 ...
- struts2框架之类型转换(参考第二天学习笔记)
类型转换 1. 什么是类型转换 刚才学习了封装请求参数,把表单数据封装到Action(模型)的属性中.表单中的数据都是String类型,但Action(模型)的属性不一定什么类型. 将来我们还需要数据 ...
- TCP与UDP区别小结
TCP(Transmission Control Protocol):传输控制协议 UDP(User Datagram Protocol):用户数据报协议 主要从连接性(Connectiv ...
- OpenStack实践系列⑧可视化服务Horizon之Dashboard演示
OpenStack实践系列⑧可视化服务Horizon之Dashboard演示 七.可视化服务Horizon之Dashboard演示 仪表板依赖于功能核心服务,包括身份,图像服务,计算和网络两种(neu ...
- most asked interview questions for C/C++
1. compared to prefix ++, postfix increment needs one more step to create a temporary variable? w ...
- IList与List的区别
List是一个类(Class),IList是一个接口(Interface),不能被实例化,只能用 IList <T> myIList =new List <T>(); List ...
- tornado的异步效果
第一种方式: import tornado.ioloop import tornado.web from tornado import gen from tornado.concurrent impo ...
- winform数据存储的方式
存储的方式有三种: 一.SQL数据库 二.Access(office 2007版本以上是需要安装驱动的) 三.XML
- yun
# Author:zhang# -*- coding:utf-8 -*-"""https://workyun.com/ 云端工作"""imp ...