上一版做了什么
- 實(shí)現(xiàn)了一個
js打包器
這一版做了什么
- 增加
loader支持 - 增加
plugin支持
為什么要增加loader和plugin
在上一篇文章中提到,webpack 就是一個 打包 js代碼的打包器。至于webpack能打包 圖片、css、less、scss 等其他文件, 那都是loader或者plugin的功能。所以,為了打包器更強(qiáng)大,需要增加loader和plugin的支持
不足的點(diǎn)
我只是寫了一個簡單的實(shí)現(xiàn),真正的webpack比我這個強(qiáng)大太多,但是基本原理是一樣的。
先來一發(fā)整體工作流程圖吧

webpack工作原理.png
再來一個架構(gòu)圖

webpack架構(gòu)圖.png
工作流程
通過上述兩個圖,可以大概描述出webpack的工作原理了。通過loader和plugin的加持,webpack可以完成各種各樣的工作。
loader支持
編譯器在獲取源碼的時候,即可對源碼進(jìn)行操作,此時,loader便可以排上用場了,在loader中,對源碼可以做一些操作,然后講源碼返回。 這也是webpack的loader的處理方式。具體修改為,在上一版的編譯器的構(gòu)建模塊函數(shù)中增加代碼。
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 獲取對應(yīng)的loader函數(shù)
const loader = require(use[len--]);
content = loader(content);
if (len >= 0) {
normalLoader();
}
}
normalLoader();
}
}
return content;
}
plugin支持
針對plugin的支持,采用了和
webpack一樣的模式,使用tapable這個庫
簡單介紹一下tapable這個庫。這是庫提供了一些觀察者處理方法,有同步hooks和異步hooks. 具體使用方法,請看 tapable
所以在編譯器的run方法執(zhí)行之前,我們需要注冊所有的觀察者. 則有了代碼:
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);
});
}
// 調(diào)用plugin注冊完的鉤子
this.hooks.afterPlugins.call();
}
剩下的,我們就是需要在編譯器的各個步驟來觸發(fā)鉤子函數(shù)了.
例如, 在編譯開始之前、編譯進(jìn)行時、編譯完成后、文件發(fā)射前、文件發(fā)射后....
這個版本里面我只做了幾個簡單的觸發(fā)
run() {
this.hooks.run.call();
this.hooks.compile.call();
// 執(zhí)行并且創(chuàng)建模塊的依賴關(guān)系
this.buildModule(path.resolve(this.root, this.entry), true);
this.hooks.afterCompile.call();
// 發(fā)射一個打包后的文件
this.emitFile();
this.hooks.emit.call();
this.hooks.done.call();
}
完整源碼
待上傳到github