上一版做了什么
- 實(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