浅析webpack源码
写在前面
webpack源码自己研究了几个星期,而且在团队内部也做了分享,之前一直没有在思否上写过文章,后面感觉思否相对于CSDN文章质量更高,因此转战思否,后面争取每周更新一篇较高质量文章,期待自己不断取得提升。
1、webpack的作用
webpack作为前端十分重要的一种打包工具,其主要作用用一句话概括:
- 将代码(包括css, ts, vue,sass)基于依赖关系打包成浏览器可以运行的多个bundle.js
基于该目的,webpack必须要实现四件事情:
- 基于acorn搜集模块依赖;
- 将所有文件基于loader转成浏览器可以运行的js
- 在打包整个生命周期过程中做很多事情(基于plugin实现)
- 最后打包(tree-shaking等)成多个bundle.js
2、tapable和acorn
粘贴一段源码,可以看到在整个编译过程多次出现类似的写法,并且在两个钩子之间并没有找到对应的实现代码,那具体钩子之中是做了什么呢?
这个时候就需要介绍下tapable库
2.1 tapable 是一个类似于nodejs 的EventEmitter 的库, 主要是控制钩子函数的发布与订阅,控制着webpack的插件系.webpack的本质就是一系列的插件运行.
- 钩子类型分为四种:
1、基本的钩子
2、waterfall: 会调用每个tap传入的函数,同时将每个函数的返回值给下一个函数
3、bail: 允许更早的退出,当某个tap的函数返回任何值,该钩子会停止其他函数执行;
4、loop: 若某个函数有返回值,则会循环之前执行的事件
在webpack中使用最多的钩子是 syncHook(同步钩子)和 AsyncSeriesHook(异步串行钩子)
钩子的三种注册方式(包含名称和对应的回调):
1、tap: 注册同步钩子
2、tapAsync: 注册带callback回调的异步钩子 (用的最多)
3、tapPromise: 注册带promise回调的异步钩子
对应的三种调用方式:
1、 call : 调用注册的同步钩子;
2、callAsync: 调用注册的带有callback回调的异步钩子
3、promise: 调用注册的带promise回调的异步钩子
看个例子
- webpack中beforeCompile钩子的声明,插件注册和调用
- 基于在webpack各个生命周期钩子中声明的插件,实现webpack的整个功能
- 看下webpack各个声明周期钩子: https://webpack.js.org/api/co...
补充一个小点: 观察者模式和发布订阅模式的区别:
观察者模式是松耦合,只有观察者和被观察者两者(比如某个值变化,调用某个函数);
发布订阅模式不存在耦合,有发布者,订阅者和经纪人(broker)
2.2 对于acorn
- acorn是一个库,实现对js的解析,将其转换成ast, 转换后实现对依赖搜集
3、webpack运行各阶段
- 首先看下Compiler和Compilation的区别
Compiler 对象: 包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;
Compilation 对象:包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。
Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。
- 找了一张网图,对各流程梳理
- 在依赖搜集,loader对代码处理,均在make阶段实现
- 几个要点:
1、webpack运行是调用compiler.run
2、webpack基于tapable实现的发布订阅模式构建(万物即插件)
3、ast是基于acron库实现
4、loader处理和依赖搜集是在make阶段实现
5、整个阶段会经历: environment,afterEnvironment,beforeRun,run, beforeCompile,Make, afterCompile 等多个阶段
以上就是对webpack源码浅析,对于大体流程做了一个梳理,但是更为精细的流程还是需要去看各插件的源码才行
转载自:https://segmentfault.com/a/1190000040531288