webpack4插件及工作流程

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

内容简介:webpack4重写了如上代码所示先是在实例化的过程中注册了三个钩子函数,在实例上调用方法时触发钩子函数。 下面介绍webpack里主要的6个最高层的实例,初始化配置,提供全局性的钩子比如

webpack4重写了 Tapable , 是webpack的插件组织的核心。它提供给各个插件钩子,在事件触发时执行这些挂载的方法。webapck的插件里必须有 apply() 方法,当其被调用的时候webpack将钩子上的方法挂载到各个事件下面有点像 nodejsEventEmitter$on

class Car {
	constructor() {
		this.hooks = {
			accelerate: new SyncHook(["newSpeed"]),
			brake: new SyncHook(),
			calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
		};
	},
    setSpeed(newSpeed) {
		this.hooks.accelerate.call(newSpeed);
	}
}
复制代码

如上代码所示先是在实例化的过程中注册了三个钩子函数,在实例上调用方法时触发钩子函数。 下面介绍webpack里主要的6个 Tapable 的实例, 它们都继承了 Tapable ,定义了一些自己的hook

Compiler

最高层的实例,初始化配置,提供全局性的钩子比如 done , compilation 。其他的 Tapable 实例需要通过其访问,如

compiler.hooks.compilation.tap(
      "myFirstWebpackPlugin",
      (compilation, params) => {
        compilation.hooks.seal.tap()
      }
    );
复制代码

Compilation

Compiler 创建,整个构建就在这里完成,进行依赖图构建,优化资源,渲染出runtime时的代码等。下面的4个实例都是发生在这个阶段。

Resolver

当你请求一个模块的时候,你将模块名或者相对地址发给模块解析器,它会去解析出绝对地址去寻找那个模块,看是否存在,如果存在则返回相应的模块信息,包括上下文等。这里的请求可以类似网络请求一样携带上查询参数之类的, Resolver 将会返回额外信息。webpack4里将 Resolver 这个实例抽出来单独发了一个包 enhanced-resolve , 抽象出来可以便于用户实现自己的 Resolver

ModuleFactory

模块工厂就是负责构造模块的实例,介绍两种 NormalModuleFactoryContextModuleFactory 。两者不同的地方在于后者用于解析动态 import() . 模块工厂主要是用于将 Resolver 解析成功的请求里的源码从文件中拿出,在内存中创建一个模块对象(NormalModule)

Parser

Parser 主要用于将代码解析成AST抽象语法:evergreen_tree:.可以在ast查看代码转换成AST后的样子。webpack默认采用 acorn 解析器,babel是 babylonParserModuleFactory 返回的对象里的代码字符串转换成AST后进行解析,发现 import 或者 require 或者 define 类似模块引用时会将这些引用信息也就是依赖添加到当前模块的对象里,这样每个模块对象里不但有自己模块的信息还包含它的依赖信息。webpack会在不仅仅会在模块声明处触发事件,它甚至会在解析到变量时也触发事件。如下在 webpack/lib/Parser.js 里可以看到如下三个钩子函数

varDeclaration: new HookMap(() => new SyncBailHook(["declaration"])),
    varDeclarationLet: new HookMap(() => new SyncBailHook(["declaration"])),
    varDeclarationConst: new HookMap(() => new SyncBailHook(["declaration"])),
复制代码

Template

负责生成运行时的代码

// 源码
// index.js
    var multiply  = require('./multiply')
    var sum = (a,b)=>{
        return a+b;
    }
    module.exports = sum;
// multiply.js
    module.exports = (a, b) => a*b

// 生成的runtime
[
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
        var multiply  = __webpack_require__(1)
        var sum = (a,b)=>{
            return a+b;
        }
        module.exports = sum;
/***/ }),
/* 1 */
/***/ (function(module, exports) {
        module.exports = (a, b) => a*b
/***/ })
];
复制代码

如上面代码所示,里面包含三个模板,分别负责chunk, module, dependency. chunk是包含多个模块的数组,就是外面数组的形式;module就是里面用立即执行函数包围的部分;dependency就是将原先 import , require 等引用模块部分转换成 __webpack_require__ .

工作流程

目前只看到模块构建那部分,后续再补充╮(╯_╰)╭。。。心得就是利用好vscode的调试 工具 多打断点~~

介绍完了这六个实例,下面大致讲下webpack的工作流程,webpack做的工作非常多,这里只挑主要的讲下。括号里的是源码所在的文件位置,上下文是 node_modules/webpack . 本流程基于webpack4.30.0版本。

  1. 首先是将配置文件读入,webpack4有默认配置 options = new WebpackOptionsDefaulter().process(options); 会以用户的配置为先。Compiler进行创建 compiler = new Compiler(options.context); 将配置里的plugin部分进行绑定调用
for (const plugin of options.plugins) {
        if (typeof plugin === "function") {
            plugin.call(compiler, compiler);
        } else {
            plugin.apply(compiler);
        }
    }
复制代码

进行其他配置设置 compiler.options = new WebpackOptionsApply().process(options, compiler); (lib/webpack.js) 2. 接着根据打包的目标(web, node, electron等)生成不同的打包模板

switch (options.target) {
        case "web":
            JsonpTemplatePlugin = require("./web/JsonpTemplatePlugin");
            ...
            break;
        case "webworker":
            ...
复制代码

因为浏览器端请求异步加载的模块会类似jsonp插入dom中 <script> 标签,而比如node端是没有dom结构的。

new EntryOptionPlugin().apply(compiler);
    compiler.hooks.entryOption.call(options.context, options.entry);
复制代码

这部分是将入口处配置添加调用 entryOption 钩子。 (lib/WebpackOptionsApply.js) 3. 根据不同接口类型调用不同的类,webpack里到处都是类

// lib/EntryOptionPlugin.js
    if (typeof entry === "string" || Array.isArray(entry)) {
        itemToPlugin(context, entry, "main").apply(compiler);
    } else if (typeof entry === "object") {
        for (const name of Object.keys(entry)) {
            itemToPlugin(context, entry[name], name).apply(compiler);
        }
    } else if (typeof entry === "function") {
        new DynamicEntryPlugin(context, entry).apply(compiler);
    }
复制代码

这里举例是单文件入口, 在compilation钩子上绑定(即 Compiler 创建 compilation 后调用)回调,指定当前依赖的模块生成方法。

compiler.hooks.compilation.tap(
        "SingleEntryPlugin",
        (compilation, { normalModuleFactory }) => {
            compilation.dependencyFactories.set(
                SingleEntryDependency,
                normalModuleFactory
            );
        }
    );
复制代码

(lib/SingleEntryPlugin.js) 4. 创建 compilation (lib/Compiler.js).

const compilation = this.createCompilation();
复制代码
// lib/SingleEntryPlugin.js
    compiler.hooks.make.tapAsync(
        "SingleEntryPlugin",
        (compilation, callback) => {
            const { entry, name, context } = this;
            const dep = SingleEntryPlugin.createDependency(entry, name);
            compilation.addEntry(context, dep, name, callback);
        }
    );
复制代码

上面这段是之前注册的,但是会在 compilation 创建完成前调用,是个异步钩子。 compilation 创建好后传入,它会将入口创建一个依赖。 5. 开始执行 addEntry() 方法,在 addEntry 方法里调用 _addModuleChain ,将当前入口文件创建模块 moduleFactory.create ,模块创建好后处理当前模块的依赖项 this.processModuleDependencies . 将依赖创建模块后再依次解析模块的依赖。(lib/Compilation.js)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

HTML5移动应用开发入门经典

HTML5移动应用开发入门经典

凯瑞恩 / 林星 / 人民邮电出版社 / 2013-3 / 55.00元

《HTML5移动应用开发入门经典》总共分为24章,以示例的方式对如何使用HTML5及相关技术进行移动应用开发做了全面而细致的介绍。《HTML5移动应用开发入门经典》首先讲解了HTML5的起源以及它为什么适用于移动设备,然后讲解了HTML5的基本元素以及所做的改进、canvas(画布)、视音频、微格式、微数据、拖曳等新增特性,还讲解了WebSocket、WebWorkers、Web存储、离线Web应......一起来看看 《HTML5移动应用开发入门经典》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

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

各进制数互转换器

html转js在线工具
html转js在线工具

html转js在线工具