webpack工作流
模拟插件
//类
module.exports = class DonePlugin {
apply(compiler) {
//钩子 像发布订阅模式
//hooks是个对象 done是个钩子
compiler.hooks.done.tap("DonePlugin", () => {
console.log("DonePlugin");
});
}
};
//类
module.exports = class RunPlugin {
apply(compiler) {
//钩子 像发布订阅模式
//监听run事件
compiler.hooks.run.tap("RunPlugin", () => {
console.log("RunPlugin");
});
}
};
模拟工作流
文件系统fs
webpack-dev-server用的是memory-fs 内存
/**
* webpack 工作流程
*/
let { SyncHook } = require("tapable");
let fs = require("fs");
let path = require("path");
class Compiler {
constructor(options) {
this.options = options;
this.hooks = {
run: new SyncHook(),
done: new SyncHook(),
};
}
run() {
//模拟工作流
//模块=>代码块=>文件
let modules = [];
let chunks = [];
let files = [];
//广播事件
this.hooks.run.call(); //触发run钩子执行
//根据配置中的entry找到入口文件
let entry = path.join(this.options.context, this.options.entry);
//从入口文件出发 调用所有配置的loader对模块进行编译 再找出该模块依赖的模块
//再递归本步骤直到所有入口依赖文件都经过本步骤处理
//1.读取模块内容 可能是less不一定是js
let entryContent = fs.readFileSync(entry, "utf8");
let entrySource = babelLoader(entryContent);
//模块module chunk代码块 bundle 文件的关系
let entryModule = { id: entry, source: entrySource };
modules.push(entryModule);
//把入口模块的代码转成抽象语法树AST 分析里面的import和require依赖
let title = path.join(this.options.context, "./src/title.js");
let titleContent = fs.readFileSync(title, "utf8");
let titleSource = babelLoader(titleContent);
//模块module chunk代码块 bundle 文件的关系
let titleModule = { id: title, source: titleSource };
modules.push(titleModule);
//根据入口和模块之间的依赖关系 组装成一个个含多个模块的chunk
let chunk = { name: "main", modules };
chunks.push(chunk);
//再把chunk转换成一个单独的文件加入到输出列表
let file = {
file: this.options.output.filename,
source: "source转义后的代码",
};
files.push(file);
//在确定输出内容后, 根据配置的确定输出的路径和文件名 把文件内容写入到文件系统
let outputPath = path.join(
this.options.output.path,
this.options.output.filename
);
fs.writeFileSync(outputPath, file.source, "utf8");
//广播事件
//在以上过程中 webpack会在特定的时间点广播出特定的事件
//插件在听到目标时间后会执行特定逻辑
//并且插件可以调用webpack提供的api改变webpack的运行结果
this.hooks.done.call();
}
done() {
this.hooks.done.call(); //触发run钩子执行
}
}
/**
* 1.初始化参数 从配置文件和shell语句中读取并合并参数 得出最终的结果
*/
let options = require("./webpack.config");
//开始编译 用上一步得到的参数初始化Compiler对象
let compiler = new Compiler(options);
//加载所有配置的插件 执行对象的run方法开始执行编译
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
plugin.apply(compiler);
}
}
//确定入口 根据配置中的entry找到入口
compiler.run();
//es6编译成es5
function babelLoader(source) {
return "let a = 'ES5'";
}
发布订阅模式
/**
* 发布订阅
* button.addEventListener("click",()=>console.log("click"))
* button.trigger("click")
*/
let { SyncHook } = require("tapable");
let hook = new SyncHook();
//注册监听
hook.tap("some name", () => {
console.log("some name");
});
//触发监听函数执行
hook.call();