上一版做了什么
- 实现了一个
js
打包器
这一版做了什么
- 增加
loader
支持 - 增加
plugin
支持
为什么要增加loader和plugin
在上一篇文章中提到,webpack
就是一个 打包 js
代码的打包器。至于webpack
能打包 图片、css、less、scss 等其他文件, 那都是loader
或者plugin
的功能。所以,为了打包器更强大,需要增加loader
和plugin
的支持
不足的点
我只是写了一个简单的实现,真正的webpack
比我这个强大太多,但是基本原理是一样的。
先来一发整体工作流程图吧
再来一个架构图
工作流程
通过上述两个图,可以大概描述出webpack
的工作原理了。通过loader
和plugin
的加持,webpack
可以完成各种各样的工作。
loader支持
编译器在获取源码的时候,即可对源码进行操作,此时,loader
便可以排上用场了,在loader
中,对源码可以做一些操作,然后讲源码返回。 这也是webpack
的loader
的处理方式。具体修改为,在上一版的编译器的构建模块函数中增加代码。
getSource(modulePath) {
const rules = this.config.module.rules;
let content = fs.readFileSync(modulePath, 'utf8');
for(let i = 0; i < rules.length; i++){
const rule = rules[i];
const { test, use } = rule;
let len = use.length - 1;
if (test.test(modulePath)) {
const normalLoader = () => {
// loader 获取对应的loader函数
const loader = require(use[len--]);
content = loader(content);
if (len >= 0) {
normalLoader();
}
}
normalLoader();
}
}
return content;
}
plugin支持
针对plugin的支持,采用了和
webpack
一样的模式,使用tapable
这个库
简单介绍一下tapable
这个库。这是库提供了一些观察者
处理方法,有同步hooks
和异步hooks
. 具体使用方法,请看 tapable
所以在编译器
的run
方法执行之前,我们需要注册所有的观察者
. 则有了代码:
constructor(config) {
this.config = config;
// 需要保存入口文件的路径
this.entryId;
// 需要保存所有模块的依赖
this.modules = {};
// 入口路径
this.entry = config.entry;
// 工作路径
this.root = process.cwd();
this.hooks = {
entryOption: new SyncHook(),
compile: new SyncHook(),
afterCompile: new SyncHook(),
afterPlugins: new SyncHook(),
run: new SyncHook(),
emit: new SyncHook(),
done: new SyncHook()
}
const { plugins = [] } = this.config;
// 注册所有的plugin
if (Array.isArray(plugins)) {
plugins.forEach(plugin => {
plugin.apply(this);
});
}
// 调用plugin注册完的钩子
this.hooks.afterPlugins.call();
}
剩下的,我们就是需要在编译器的
各个步骤来触发
钩子函数了.
例如, 在编译开始之前、编译进行时、编译完成后、文件发射前、文件发射后....
这个版本里面我只做了几个简单的触发
run() {
this.hooks.run.call();
this.hooks.compile.call();
// 执行并且创建模块的依赖关系
this.buildModule(path.resolve(this.root, this.entry), true);
this.hooks.afterCompile.call();
// 发射一个打包后的文件
this.emitFile();
this.hooks.emit.call();
this.hooks.done.call();
}
完整源码
待上传到github