[转] 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模块文件

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是用来生成结果代码的。入口是compilationcreateChunkAssets方法。


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源代码

ModuleTemplate源代码

参考资料

原文地址:https://www.cnblogs.com/chris-oil/p/9833741.html

时间: 2024-10-08 22:20:00

[转] webpack之plugin内部运行机制的相关文章

MFC第一节-windows程序内部运行机制

一.窗口 设计窗口类时: 1 typedef struct _WNDCLASS{ 2 UINT style; //如水平.垂直变化是否重绘,禁用Close,检测双击 3 WNDPROC lpfnWndProc;//窗口过程函数句柄 4 int cbClsExtra;//类附加内存 5 int cbWndExtra;//窗口附加内存 6 HANDLE hInstance;//实例句柄 7 HANDLE hIcon;//图标 8 HCURSOR hCursor;//光标 9 HBRUSH hbrBa

再理解Windows程序内部运行机制

参照孙鑫<VC++深入详解> 创建Win32程序的步骤: 1. 编写WinMain函数 2. 设计窗口类(WNDCLASS) 3. 注册窗口类 4. 创建窗口 5. 显示并更新窗口(ShowWindow(hwnd,SW_SHOWNORMAL);UpdateWindow(hwnd);) 6. 消息循环(不断地从消息队列中取出消息,并进行响应) 7. 窗口过程函数(处理发送给窗口的消息) 测试代码如下(在VS2010编译通过): #include <stdafx.h> #include

IIS 内部运行机制

ASP.NET是一个非常强大的构建Web应用的平台,它提供了极大的灵活性和能力以致于可以用它来构建所有类型的Web应用. 绝大多数的人只熟悉高层的框架如: WebForms 和 WebServices --这些都在ASP.NET层次结构在最高层. 这篇文章的资料收集整理自各种微软公开的文档,通过比较 IIS5.IIS6.IIS7 这三代 IIS 对请求的处理过程, 让我们熟悉 ASP.NET的底层机制并对请求(request)是怎么从Web服务器传送到ASP.NET运行时有所了解.通过对底层机制

Windows程序内部运行机制

Windows程序内部运行机制 一.        API与SDK Windows操作系统提供了各种各样的函数,以方便我们开发Windows应用程序,这些函数是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称为API函数.我们在编写Windows程序时所说的API函数,就是指系统提供的函数,所有主要的Windows函数都在Windows.h头文件中进行了说明. SDK的全称是Software Development Kit

【vc】1_Windows程序内部运行机制

创建一个Win32应用程序步骤: 1.编写WinMain函数; 2.创建窗口(步骤如下): a.设计(一个)窗口类(WNDCLASS) b.注册(该)窗口类. c.创建窗口. d.显示并更新窗口. 3.编写消息循环. 4.编写窗口过程函数. 1 //WinMain.cpp 2 #include <windows.h> 3 #include <stdio.h> 4 5 LRESULT CALLBACK WinAzeProc( 6 HWND hwnd, // handle to win

Windows程序内部运行机制 转自http://www.cnblogs.com/zhili/p/WinMain.html

一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序就可以了,微软帮我们做了非常好的封装,以至于对于很多.NET开发人员至今也不清楚Windows 平台下程序运行的内部机制,所以本专题将深入剖析下Windows 程序的内部运行机制. 二.Windows平台下几个基础概念 有朋友会问,理解了程序运行的内部机制有什么用,因为在我们实际开发中用得微软提供的

第一章 Windows程序内部运行机制(4)WinMain函数

WinMain函数相当于main函数,作为Windows程序的入口函数.当WinMain结束或返回时,Windows程序结束. 一个win32应用程序,该程序创建一个窗口并在窗口中响应键盘与鼠标消息,程序的实现步骤为: 1.WinMain函数的定义:2.创建一个窗口:3.进行消息循环:4.编写窗口过程 WinMain函数的定义: int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, in

深入理解ASP.NET的内部运行机制(转)

WebForms和WebServices作为.NET平台构建Web程序的两大利器,以其开发简单.易于部署的特点得到了广泛的应用,但殊不知微软公司在背后为我们做了大量的基础性工作,以至于我们开发人员只需简单地拖拖控件.写写一些页面级的代码就可以轻松地实现一些简单的应用程序.当然这种封装也不是没有好处的,至少从开发的角度而言它就可以极大地降低开发的难度,但是这种过度的封装使得我们开发人员当遇到有可能由底层引起的问题时就会束手无策,而且也会使得我们对知识的掌握和理解只停留在了表面而不得其内在本质.正是

WWindows程序内部运行机制

Windows程序内部运行机制 ü      关键概念 1.句柄 句柄是整个Windows编程的基础.一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等. 2.消息与消息队列 windows程序设计是种事件驱动方式的程序设计,主要基于消息的.例如,当用户在窗口中华图时,按下鼠标左键,此时os会感知到这一事件,于是将此事件包装成一个消息,投递到应用程序的消息队