先上一张流程图,小伙伴可以跟着这个流程图结合代码往下走:
再来一张大神画的图:
开始
代码语言:javascript复制git clone https://github.com/webpack/webpack.git
yarn
yarn setup
打包
代码语言:javascript复制node ./bin/webpack.js
bin/webpack.js
代码语言:javascript复制//判断有没有安装webpack-cli,没有话的就提示是否需要安装
const question = `Do you want to install 'webpack-cli' (yes/no): `;
//确定有webpack-cli之后开始执行
require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
webpack-cli/bin/cli.js
代码语言:javascript复制//创建compiler编译引擎
let compiler;
try {
compiler = webpack(options);
} catch (err) {
}
compiler.run((err, stats) => {}
整个made过程
lib/webpack.js
代码语言:javascript复制const webpack = (options, callback) => {
let compiler;
compiler = createCompiler(options);
watch = options.watch;
watchOptions = options.watchOptions || {};
return compiler;
};
//创建编译器
const createCompiler = options => {
//获取默认的webpack参数
options = new WebpackOptionsDefaulter().process(options);
const compiler = new Compiler(options.context);
compiler.options = options;
//配置全局api插件比如(fs文件api、infrastructureLogger日志log、)
new NodeEnvironmentPlugin({
infrastructureLogging: options.infrastructureLogging
}).apply(compiler);
// 执行我们在配置文件中配置的所有插件
if (Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
//开启默认的所有插件(比较重要)
compiler.options = new WebpackOptionsApply().process(options, compiler);
return compiler;
};
lib/WebpackOptionsApply.js
代码语言:javascript复制 //设置默认的一些插件
process(options, compiler) {
//入口文件插件注册
new EntryOptionPlugin().apply(compiler);
//调用入口文件插件注册钩子函数
compiler.hooks.entryOption.call(options.context, options.entry);
}
lib/EntryOptionPlugin.js
代码语言:javascript复制module.exports = class EntryOptionPlugin {
/**
* @param {Compiler} compiler the compiler instance one is tapping into
* @returns {void}
*/
apply(compiler) {
//注册入口文件插件注册钩子函数
compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
const applyEntryPlugins = (entry, name) => {
if (typeof entry === "string") {
//调用入口插件
new EntryPlugin(context, entry, name).apply(compiler);
} else if (Array.isArray(entry)) {
for (const item of entry) {
applyEntryPlugins(item, name);
}
}
};
//options的entry支持string||array比如:(entry: "src/main.js")||(entry: ["pollyfill.js","src/main.js"])
if (typeof entry === "string" || Array.isArray(entry)) {
applyEntryPlugins(entry, "main");
//options的entry支持object类型比如:(entry:{main: "src/main.js"})
} else if (typeof entry === "object") {
for (const name of Object.keys(entry)) {
applyEntryPlugins(entry[name], name);
}
//options的entry支持function类型比如:(entry:()=>"src/main.js")})
} else if (typeof entry === "function") {
new DynamicEntryPlugin(context, entry).apply(compiler);
}
return true;
});
}
};
EntryPlugin.js
代码语言:javascript复制apply(compiler) {
//注册compiler的make钩子函数(后面会用到)
compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
const { entry, name, context } = this;
const dep = EntryPlugin.createDependency(entry, name);
compilation.addEntry(context, dep, name, err => {
callback(err);
});
});
}
lib/Compiler.js:
代码语言:javascript复制//开始编译
run(callback) {
//整个打包编译执行完毕回调
const finalCallback = (err, stats) => {
if (logger) logger.time("beginIdle");
this.cache.beginIdle();
if (logger) logger.timeEnd("beginIdle");
this.running = false;
if (err) {
this.hooks.failed.call(err);
}
if (callback !== undefined) callback(err, stats);
this.hooks.afterDone.call(stats);
};
//编译完毕回调
const onCompiled = (err, compilation) => {
if (err) return finalCallback(err);
logger = compilation.getLogger("webpack.Compiler");
logger.time("emitAssets");
//执行打包操作
this.emitAssets(compilation, err => {
logger.timeEnd("emitAssets");
if (err) return finalCallback(err);
if (compilation.hooks.needAdditionalPass.call()) {
compilation.needAdditionalPass = true;
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
logger.time("done hook");
this.hooks.done.callAsync(stats, err => {
logger.timeEnd("done hook");
if (err) return finalCallback(err);
this.hooks.additionalPass.callAsync(err => {
if (err) return finalCallback(err);
this.compile(onCompiled);
});
});
return;
}
});
};
this.cache.endIdle(err => {
if (err) return finalCallback(err);
//调用编译打包前钩子函数
this.hooks.beforeRun.callAsync(this, err => {
if (err) return finalCallback(err);
//调用运行中钩子函数
this.hooks.run.callAsync(this, err => {
if (err) return finalCallback(err);
//读取Records文件
this.readRecords(err => {
if (err) return finalCallback(err);
//开始编译
this.compile(onCompiled);
});
});
});
});
}
//编译
compile(callback) {
//创建编译器的参数(重要)
const params = this.newCompilationParams();
//创建一个编译器
const compilation = this.newCompilation(params);
//执行前面## EntryPlugin.js注册的make钩子函数并把编译器传递给插件
this.hooks.make.callAsync(compilation, err => {
}
//创建编译器的参数
newCompilationParams() {
const params = {
normalModuleFactory: this.createNormalModuleFactory(), //创建一个模块工厂(主要用于模块的构建,也就是我们说的js文件等等)
contextModuleFactory: this.createContextModuleFactory() //创建一个上下文工厂(主要用于整个工程的文件路径加载)
};
return params;
}
//创建模块工厂
createNormalModuleFactory() {
const normalModuleFactory = new NormalModuleFactory({
context: this.options.context,
fs: this.inputFileSystem,
resolverFactory: this.resolverFactory,
options: this.options.module || {}
});
this.hooks.normalModuleFactory.call(normalModuleFactory);
return normalModuleFactory;
}
前面注册的钩子函数 lib/EntryPlugin.js
代码语言:javascript复制compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
//执行编译器的addEntry方法加载入口文件
compilation.addEntry(context, dep, name, err => {
callback(err);
});
lib/Compilation.js
代码语言:javascript复制//加载入口文件
addEntry(context, entry, name, callback) {
this.addModuleChain(context, entry, (err, module) => {
if (err) {
this.hooks.failedEntry.call(entry, name, err);
return callback(err);
}
this.hooks.succeedEntry.call(entry, name, module);
return callback(null, module);
});
}
addModuleChain(context, dependency, callback) {
this.handleModuleCreation(
{
factory: moduleFactory,
dependencies: [dependency],
originModule: null,
context
},
}
handleModuleCreation(
{ factory, dependencies, originModule, context },
callback
) {
//遍历工程中所有的模块并且创建模块
this.factorizeModule(
//创建模块
this.buildModule(module, err => {
}
//开始创建模块
_buildModule(module, callback) {
//开始执行模块的构建方法
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
err => {
}
//遍历工程中所有的模块
_factorizeModule(
{ currentProfile, factory, dependencies, originModule, context },
callback
) {
if (currentProfile !== undefined) {
currentProfile.markFactoryStart();
}
//调用前面创建的NormalModuleFactory的create方法
factory.create(
{
lib/NormalModuleFactory.js
代码语言:javascript复制//构造方法
constructor({ context, fs, resolverFactory, options }) {
//注册开始构建模块钩子函数
this.hooks.factorize.tapAsync(
{
name: "NormalModuleFactory",
stage: 100
},
(resolveData, callback) => {
//触发开始解析模块钩子函数(注册“解析模块钩子函数”也在NormalModuleFactory.js里面,主要就是获取我们配置的“loader”,重要)
this.hooks.resolve.callAsync(resolveData, (err, result) => {
//模块解析完毕后
this.hooks.afterResolve.callAsync(resolveData, (err, result) => {
if (err) return callback(err);
if (typeof result === "object")
throw new Error(deprecationChangedHookMessage("afterResolve"));
// Ignored
if (result === false) return callback();
const createData = resolveData.createData;
let createdModule = this.hooks.createModule.call(createData);
if (!createdModule) {
if (!resolveData.request) {
return callback(new Error("Empty dependency (no request)"));
}
createdModule = new NormalModule(createData);
}
//创建一个新的模块(包含模块的各种信息,比如模块的路径、能被哪些loader加载器加载等等)
createdModule = this.hooks.module.call(
createdModule,
createData,
resolveData
);
return callback(null, createdModule);
});
});
}
);
}
//创建工程的所有模块
create(data, callback) {
//获取所有的模块
const dependencies = /** @type {ModuleDependency[]} */ (data.dependencies);
if (this.unsafeCache) {
const cacheEntry = dependencyCache.get(dependencies[0]);
if (cacheEntry) return callback(null, cacheEntry);
}
//触发开始构建模块钩子函数(在构造函数中注册)
this.hooks.factorize.callAsync(resolveData, (err, module) => {
});
});
}
lib/NormalModule.js
代码语言:javascript复制 //被上面的Compilation.js中的_buildModule方法中的module.build调用
build(options, compilation, resolver, fs, callback) {
return this.doBuild(options, compilation, resolver, fs, err => {
}
}
//开始构建
doBuild(options, compilation, resolver, fs, callback) {
//处理loader加载过后的源码
const processResult = (err, result) => {
this._source = this.createSource(
options.context,
this.binary ? asBuffer(source) : asString(source),
sourceMap,
compilation.compiler.root
);
}
//运行我们在配置文件中配置的所有loader,比如babel-loader,然后返回处理完毕后的源码
runLoaders(
{
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: fs.readFile.bind(fs)
},
(err, result) => {
if (!result) {
processResult(
err || new Error("No result from loader-runner processing"),
null
);
}
processResult(err, result.result);
}
);
}
//创建源码
createSource(context, content, sourceMap, associatedObjectForCache) {
if (Buffer.isBuffer(content)) {
//获取构建过后的源码
return new RawSource(content);
}
}
整个made过程结束
seal封装过程开始(1、优化我们编译过后的代码(代码混淆、分包优化等等)、2、生成我们最后需要的打包过后的文件)
Compiler.js文件
代码语言:javascript复制compile(callback) {
logger.time("seal compilation");
//开始封装并打包文件
compilation.seal(err => {
}
}
lib/Compilation.js(重要,了解webpack的插件)
代码语言:javascript复制/**
* @param {Callback} callback signals when the call finishes
* @returns {void}
*/
seal(callback) {
const chunkGraph = new ChunkGraph(this.moduleGraph);
this.chunkGraph = chunkGraph;
for (const module of this.modules) {
ChunkGraph.setChunkGraphForModule(module, chunkGraph);
}
this.hooks.seal.call();
while (this.hooks.optimizeDependencies.call(this.modules)) {
/* empty */
}
this.hooks.afterOptimizeDependencies.call(this.modules);
this.hooks.beforeChunks.call();
for (const [name, dependencies] of this.entryDependencies) {
const chunk = this.addChunk(name);
chunk.name = name;
const entrypoint = new Entrypoint(name);
entrypoint.setRuntimeChunk(chunk);
this.namedChunkGroups.set(name, entrypoint);
this.entrypoints.set(name, entrypoint);
this.chunkGroups.push(entrypoint);
connectChunkGroupAndChunk(entrypoint, chunk);
for (const dep of dependencies) {
entrypoint.addOrigin(null, { name }, dep.request);
const module = this.moduleGraph.getModule(dep);
if (module) {
chunkGraph.connectChunkAndModule(chunk, module);
chunkGraph.connectChunkAndEntryModule(chunk, module, entrypoint);
this.assignDepth(module);
}
}
}
buildChunkGraph(
this,
/** @type {Entrypoint[]} */ (this.chunkGroups.slice())
);
this.hooks.afterChunks.call(this.chunks);
this.hooks.optimize.call();
while (this.hooks.optimizeModules.call(this.modules)) {
/* empty */
}
this.hooks.afterOptimizeModules.call(this.modules);
while (this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups)) {
/* empty */
}
this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
if (err) {
return callback(
makeWebpackError(err, "Compilation.hooks.optimizeTree")
);
}
this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
while (this.hooks.optimizeChunkModules.call(this.chunks, this.modules)) {
/* empty */
}
this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules);
const shouldRecord = this.hooks.shouldRecord.call() !== false;
this.hooks.reviveModules.call(this.modules, this.records);
this.hooks.beforeModuleIds.call(this.modules);
this.hooks.moduleIds.call(this.modules);
this.hooks.optimizeModuleIds.call(this.modules);
this.hooks.afterOptimizeModuleIds.call(this.modules);
this.hooks.reviveChunks.call(this.chunks, this.records);
this.hooks.beforeChunkIds.call(this.chunks);
this.hooks.chunkIds.call(this.chunks);
this.hooks.optimizeChunkIds.call(this.chunks);
this.hooks.afterOptimizeChunkIds.call(this.chunks);
this.sortItemsWithChunkIds();
if (shouldRecord) {
this.hooks.recordModules.call(this.modules, this.records);
this.hooks.recordChunks.call(this.chunks, this.records);
}
this.hooks.optimizeCodeGeneration.call(this.modules);
this.hooks.beforeModuleHash.call();
this.createModuleHashes();
this.hooks.afterModuleHash.call();
this.hooks.beforeCodeGeneration.call();
this.codeGenerationResults = this.codeGeneration();
this.hooks.afterCodeGeneration.call();
this.hooks.beforeRuntimeRequirements.call();
this.processRuntimeRequirements(this.entrypoints.values());
this.hooks.afterRuntimeRequirements.call();
this.hooks.beforeHash.call();
this.createHash();
this.hooks.afterHash.call();
if (shouldRecord) {
this.hooks.recordHash.call(this.records);
}
this.clearAssets();
this.hooks.beforeModuleAssets.call();
this.createModuleAssets();
const cont = () => {
this.hooks.additionalChunkAssets.call(this.chunks);
this.summarizeDependencies();
if (shouldRecord) {
this.hooks.record.call(this, this.records);
}
this.hooks.additionalAssets.callAsync(err => {
if (err) {
return callback(
makeWebpackError(err, "Compilation.hooks.additionalAssets")
);
}
this.hooks.optimizeChunkAssets.callAsync(this.chunks, err => {
if (err) {
return callback(
makeWebpackError(err, "Compilation.hooks.optimizeChunkAssets")
);
}
this.hooks.afterOptimizeChunkAssets.call(this.chunks);
this.hooks.optimizeAssets.callAsync(this.assets, err => {
if (err) {
return callback(
makeWebpackError(err, "Compilation.hooks.optimizeAssets")
);
}
this.hooks.afterOptimizeAssets.call(this.assets);
if (this.hooks.needAdditionalSeal.call()) {
this.unseal();
return this.seal(callback);
}
this.hooks.finishAssets.callAsync(this.assets, err => {
if (err) {
return callback(
makeWebpackError(err, "Compilation.hooks.finishAssets")
);
}
this.hooks.afterFinishAssets.call(this.assets);
this.cache.storeBuildDependencies(
this.buildDependencies,
err => {
if (err) {
return callback(err);
}
return this.hooks.afterSeal.callAsync(callback);
}
);
});
});
});
});
};
if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
this.hooks.beforeChunkAssets.call();
this.createChunkAssets(err => {
if (err) {
return callback(err);
}
cont();
});
} else {
cont();
}
});
}
lib/Compilation.js
代码语言:javascript复制 seal(callback) {
//根据每个模块创建asset文件
this.createModuleAssets();
//提交所有的asset
this.emitAsset(
fileName,
module.buildInfo.assets[assetName],
assetsInfo ? assetsInfo.get(assetName) : undefined
);
}
//提交asset
emitAsset(file, source, assetInfo = {}) {
//把所有的asset存在compilation对象的assets属性中
this.assets[file] = source;
this.assetsInfo.set(file, assetInfo);
}
整个seal过程结束
lib/Compiler.js(emitAssets生成打包过后的文件过程开始)
代码语言:javascript复制compile(callback) {
compilation.seal(err => {
//整个seal过程执行完毕后开始执行callback方法
return callback(null, compilation);
}
run(callback) {
//seal过程执行完毕后执行onCompiled方法
const onCompiled = (err, compilation) => {
//如果有错误的话,就直接结束
if (this.hooks.shouldEmit.call(compilation) === false) {
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
});
return;
}
process.nextTick(() => {
//开始提交assets
this.emitAssets(compilation, err => {
this.hooks.done.callAsync(stats, err => {
//webpack整个过程结束
return finalCallback(null, stats);
});
});
};
this.readRecords(err => {
//整个seal过程执行完毕后开始执行onCompiled方法
this.compile(onCompiled);
});
//提交并生成打包出来的所有文件
emitAssets(compilation, callback) {
let outputPath;
//开始提交(生成打包过后的文件)
this.hooks.emit.callAsync(compilation, err => {
//获取输出文件的目标地址
outputPath = compilation.getPath(this.outputPath, {});
//生成目标文件夹
mkdirp(this.outputFileSystem, outputPath, emitFiles);
});
}
//输入所有的文件到指定文件夹
const emitFiles = err => {
asyncLib.forEachLimit(
//获取所有的文件
compilation.getAssets(),
15,
//列出打包过后的文件
({ name: file, source, info }, callback) => {
if (targetFile.match(//|\/)) {
const fs = this.outputFileSystem;
const dir = dirname(fs, join(fs, outputPath, targetFile));
mkdirp(fs, dir, writeOut);
} else {
//输出文件
writeOut();
}
}
}
}