_initBuildHash(compilation)
作用:
初始化模块的构建哈希值,作为当前模块内容的唯一标识。该哈希将用于缓存判断与增量构建。
逻辑概览:
- 使用配置中定义的哈希算法创建哈希对象。
- 如果模块已有
_source
,将其内容更新进哈希。 - 将模块的
buildMeta
元信息序列化后加入哈希。 - 生成最终哈希字符串并赋值给
buildInfo.hash
。
js
/**
* 初始化构建哈希,用于标识模块的唯一内容版本
* @param {Compilation} compilation compilation 对象
* @private
*/
_initBuildHash(compilation) {
const hash = createHash(
/** @type {Algorithm} */
(compilation.outputOptions.hashFunction) // 使用配置中指定的哈希算法
);
if (this._source) {
hash.update("source"); // 标记源代码部分
this._source.updateHash(hash); // 用模块源代码更新哈希
}
hash.update("meta"); // 标记元信息部分
hash.update(JSON.stringify(this.buildMeta)); // 把 buildMeta 对象序列化后更新哈希
/** @type {BuildInfo} */
(this.buildInfo).hash = /** @type {string} */ (hash.digest("hex")); // 生成最终的 hex 字符串作为哈希
}
build(options, compilation, resolver, fs, callback)
作用:
模块的主构建流程,执行 loader、解析源码、生成依赖、创建快照等关键步骤。
逻辑概览:
-
初始化模块状态(清除旧数据:源码、错误、依赖、AST等)。
-
准备
buildMeta
和buildInfo
两个构建数据结构。 -
调用私有方法
_doBuild()
执行真正的构建。 -
构建完成后:
- 调用
parser
对源码进行语法分析,提取依赖。 - 执行相关钩子(如
beforeParse
)。 - 如果解析失败,记录错误并终止。
- 排序依赖、生成构建哈希。
- 调用
-
检查依赖是否都是绝对路径,若不是添加警告。
-
生成文件系统快照(用于缓存和监听)。
js
/**
* 模块构建主入口,执行模块的构建流程
* @param {WebpackOptions} options webpack 配置对象
* @param {Compilation} compilation 当前 compilation 实例
* @param {ResolverWithOptions} resolver 模块解析器
* @param {InputFileSystem} fs 文件系统对象
* @param {function(WebpackError=): void} callback 构建完成回调
* @returns {void}
*/
build(options, compilation, resolver, fs, callback) {
this._forceBuild = false;
this._source = null; // 清空上次构建的源码缓存
if (this._sourceSizes !== undefined) this._sourceSizes.clear(); // 清除源码大小缓存
this._sourceTypes = undefined;
this._ast = null;
this.error = null;
// 清空构建错误和警告
this.clearWarningsAndErrors();
// 清除依赖项和 block 信息
this.clearDependenciesAndBlocks();
// 重置构建元信息对象
this.buildMeta = {};
// 初始化构建信息
this.buildInfo = {
cacheable: false,
parsed: true,
fileDependencies: undefined,
contextDependencies: undefined,
missingDependencies: undefined,
buildDependencies: undefined,
valueDependencies: undefined,
hash: undefined,
assets: undefined,
assetsInfo: undefined
};
const startTime = compilation.compiler.fsStartTime || Date.now();
// 获取构建相关钩子
const hooks = NormalModule.getCompilationHooks(compilation);
return this._doBuild(options, compilation, resolver, fs, hooks, err => {
if (err) {
// 构建失败,记录错误并初始化哈希
this.markModuleAsErrored(err);
this._initBuildHash(compilation);
return callback();
}
/**
* 构建解析错误处理函数
* @param {Error} e 错误对象
*/
const handleParseError = e => {
const source = /** @type {Source} */ (this._source).source(); // 获取源码
const loaders = this.loaders.map(item =>
contextify(
/** @type {string} */ (options.context),
item.loader,
compilation.compiler.root
)
);
const error = new ModuleParseError(source, e, loaders, this.type); // 创建解析错误对象
this.markModuleAsErrored(error); // 标记模块构建失败
this._initBuildHash(compilation); // 初始化构建哈希
return callback();
};
/**
* 构建完成处理函数
*/
const handleBuildDone = () => {
try {
hooks.beforeSnapshot.call(this); // 执行构建快照前钩子
} catch (err) {
this.markModuleAsErrored(/** @type {WebpackError} */ (err));
return callback();
}
const snapshotOptions = compilation.options.snapshot.module;
const { cacheable } = /** @type {BuildInfo} */ (this.buildInfo);
// 如果模块不可缓存或未配置快照,直接回调
if (!cacheable || !snapshotOptions) {
return callback();
}
/** @type {undefined | Set<string>} */
let nonAbsoluteDependencies;
/**
* 检查依赖集合中的路径是否为绝对路径
* 若非绝对路径,尝试转换为绝对路径并加入警告
* @param {LazySet<string>} deps 依赖集合
*/
const checkDependencies = deps => {
for (const dep of deps) {
if (!ABSOLUTE_PATH_REGEX.test(dep)) {
if (nonAbsoluteDependencies === undefined)
nonAbsoluteDependencies = new Set();
nonAbsoluteDependencies.add(dep);
deps.delete(dep);
try {
const depWithoutGlob = dep.replace(/[\\/]?\*.*$/, "");
const absolute = join(
compilation.fileSystemInfo.fs,
/** @type {string} */ (this.context),
depWithoutGlob
);
if (absolute !== dep && ABSOLUTE_PATH_REGEX.test(absolute)) {
(depWithoutGlob !== dep
? /** @type {NonNullable<KnownBuildInfo["contextDependencies"]>} */
(
/** @type {BuildInfo} */ (this.buildInfo)
.contextDependencies
)
: deps
).add(absolute);
}
} catch (_err) {
// 忽略路径转换错误
}
}
}
};
const buildInfo = /** @type {BuildInfo} */ (this.buildInfo);
const fileDependencies = /** @type {NonNullable<KnownBuildInfo["fileDependencies"]>} */ (buildInfo.fileDependencies);
const contextDependencies = /** @type {NonNullable<KnownBuildInfo["contextDependencies"]>} */ (buildInfo.contextDependencies);
const missingDependencies = /** @type {NonNullable<KnownBuildInfo["missingDependencies"]>} */ (buildInfo.missingDependencies);
checkDependencies(fileDependencies);
checkDependencies(missingDependencies);
checkDependencies(contextDependencies);
// 如果存在非绝对路径,添加警告
if (nonAbsoluteDependencies !== undefined) {
const InvalidDependenciesModuleWarning = getInvalidDependenciesModuleWarning();
this.addWarning(
new InvalidDependenciesModuleWarning(this, nonAbsoluteDependencies)
);
}
// 创建文件系统快照,后续用于缓存判断和监听
compilation.fileSystemInfo.createSnapshot(
startTime,
fileDependencies,
contextDependencies,
missingDependencies,
snapshotOptions,
(err, snapshot) => {
if (err) {
this.markModuleAsErrored(err);
return;
}
// 快照完成后清除原始依赖信息
buildInfo.fileDependencies = undefined;
buildInfo.contextDependencies = undefined;
buildInfo.missingDependencies = undefined;
buildInfo.snapshot = snapshot;
return callback();
}
);
};
/**
* 解析完成后处理函数
*/
const handleParseResult = () => {
// 对模块依赖排序
this.dependencies.sort(
concatComparators(
compareSelect(a => a.loc, compareLocations),
keepOriginalOrder(this.dependencies)
)
);
this._initBuildHash(compilation); // 生成构建哈希
this._lastSuccessfulBuildMeta = /** @type {BuildMeta} */ (this.buildMeta); // 缓存元信息
return handleBuildDone();
};
// 解析前钩子
try {
hooks.beforeParse.call(this);
} catch (err) {
this.markModuleAsErrored(/** @type {WebpackError} */ (err));
this._initBuildHash(compilation);
return callback();
}
// 判断是否禁止解析模块
const noParseRule = options.module && options.module.noParse;
if (this.shouldPreventParsing(noParseRule, this.request)) {
/** @type {BuildInfo} */ (this.buildInfo).parsed = false;
this._initBuildHash(compilation);
return handleBuildDone();
}
// 尝试使用 parser 解析模块源码
try {
const source = /** @type {Source} */ (this._source).source();
/** @type {Parser} */ (this.parser).parse(this._ast || source, {
source,
current: this,
module: this,
compilation,
options
});
} catch (parseErr) {
handleParseError(/** @type {Error} */ (parseErr));
return;
}
// 解析完成
handleParseResult();
});
}
getConcatenationBailoutReason(context)
作用:
用于模块串联(scope hoisting)优化时判断模块是否能被内联。
逻辑概览:
- 交由当前模块使用的
generator
判断是否支持串联。 - 如果不能串联,返回一个具体的阻止原因字符串。
js
/**
* 获取不能进行模块串联优化的原因
* @param {ConcatenationBailoutReasonContext} context 优化上下文
* @returns {string | undefined} 返回阻止串联的原因或 undefined(表示可以串联)
*/
getConcatenationBailoutReason(context) {
return /** @type {Generator} */ (
this.generator
).getConcatenationBailoutReason(this, context);
}
getSideEffectsConnectionState(moduleGraph)
作用:
用于 tree-shaking,判断该模块是否含有副作用(side effects)。
逻辑概览:
- 优先从
factoryMeta
和buildMeta
判断是否声明了无副作用。 - 如果声明为无副作用,则递归检查其依赖的副作用状态。
- 如果任意依赖有副作用,则本模块也视为有副作用。
- 缓存和标记模块是否添加过优化提示(避免重复)。
js
/**
* 判断模块在仅被副作用引用时是否应该被保留
* @param {ModuleGraph} moduleGraph 模块图
* @returns {ConnectionState} 连接状态
*/
getSideEffectsConnectionState(moduleGraph) {
if (this.factoryMeta !== undefined) {
if (this.factoryMeta.sideEffectFree) return false;
if (this.factoryMeta.sideEffectFree === false) return true;
}
if (this.buildMeta !== undefined && this.buildMeta.sideEffectFree) {
if (this._isEvaluatingSideEffects)
return ModuleGraphConnection.CIRCULAR_CONNECTION;
this._isEvaluatingSideEffects = true;
/** @type {ConnectionState} */
let current = false;
for (const dep of this.dependencies) {
const state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
if (state === true) {
// 添加优化提示
if (
this._addedSideEffectsBailout === undefined
? ((this._addedSideEffectsBailout = new WeakSet()), true)
: !this._addedSideEffectsBailout.has(moduleGraph)
) {
this._addedSideEffectsBailout.add(moduleGraph);
moduleGraph
.getOptimizationBailout(this)
.push(() => `Dependency (${dep.type}) with side effects at ${formatLocation(dep.loc)}`);
}
this._isEvaluatingSideEffects = false;
return true;
} else if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
current = ModuleGraphConnection.addConnectionStates(current, state);
}
}
this._isEvaluatingSideEffects = false;
return current;
}
return true;
}
getSourceTypes()
作用:
获取模块生成的资源类型(如 'javascript'
、'css'
等)。
逻辑概览:
- 若
_sourceTypes
缓存不存在,则调用generator.getTypes()
获取类型集合。 - 返回类型集合(通常是
Set
类型)。
js
/**
* 获取模块生成的源代码类型(如 javascript、css 等)
* @returns {SourceTypes} 类型集合(不可修改)
*/
getSourceTypes() {
if (this._sourceTypes === undefined) {
this._sourceTypes = /** @type {Generator} */ (this.generator).getTypes(this);
}
return this._sourceTypes;
}