时至今日,webpack仍然是最火和最稳定的前端打包构建工具。但是在平常的业务开发中我们很少接触到其内部原理,最多也仅仅停留在使用常用的配置层面,对webpack整个工作没有一个清晰的认知,所以本文实现一个简易的webpack,旨在了解webpack其和核心流程与思想。
Tapable
webpack内部使用了tapable.
- tapable 是一个类似于 Node.js 中的 EventEmitter 的库,但更专注于自定义事件的触发和处理
- webpack 通过 tapable 将实现与流程解耦,所有具体实现通过插件的形式存在
大致像这样
class SyncHook {
constructor() {
this.taps = [];}
tap(name, fn) {
this.taps.push(fn);}
call() {
this.taps.forEach((tap) => tap());}
}
let hook = new SyncHook();
hook.tap("some name", () => {
console.log("some name");
});
class Plugin {
apply() {
hook.tap("Plugin", () => {
console.log("Plugin ");
});}
}
new Plugin().apply();
hook.call();
webpack编译流程梳理
1.初始化参数:从配置文件和 Shell 语句中读取并合并参数,得出最终的配置对象
2.用上一步得到的参数初始化 Compiler 对象
3.加载所有配置的插件
4.执行对象的 run 方法开始执行编译
5.根据配置中的entry
找出入口文件
6.从入口文件出发,调用所有配置的Loader
对模块进行编译
7.再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
8.根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk
9.再把每个 Chunk 转换成一个单独的文件加入到输出列表
10.在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,webpack插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/1a718cfb6d78a6c9d83cb7c2ef7f97ee.png)
具体流程实现
创建目录
![](https://i-blog.csdnimg.cn/blog_migrate/e08d6b71b0ad8230d173f13aca7f5ee5.png)
src/entry1.js
const title = require('./title');
console.log('entry1', title);
src/entry2.js
const title = require('./title');
console.log('entry2', title);
src/title.js
module.exports = 'title';
debugger.js(我们最后执行这个文件来调用自己实现的webpack)
const webpack = require('./webpack');
const webpackConfig = require('./webpack.config');
//这是编译器对象代表整个编译过程
const compiler = webpack(webpackConfig);
//4.执行对象的 run 方法开始执行编译
compiler.run((err, stats) => {
console.log(err);
//stats是一个对象,记录了整个编译 过程 和产出的内容
console.log(
stats.toJson({
assets: true, //输出打包出来的文件或者说资源 main.js
chunks: true, //生成的代码块
modules: true, //打包的模块
}));
});
webpack.config.js
const path = require('path');
const RunPlugin = require('./plugins/run-plugin');
const DonePlugin = require('./plugins/done-plugin');
module.exports =