Webpack5 原理剖析与实现
整体架构设计
Webpack5 采用了事件驱动的插件架构,整个构建过程通过一系列生命周期钩子来协调各个组件的工作。其核心架构可以分为以下几个层次:
架构概览
javascript
// Webpack5 核心架构组件
const webpack = require('webpack');
class WebpackCore {
constructor(options) {
// 1. 配置处理
this.options = this.processOptions(options);
// 2. 创建 Compiler 实例
this.compiler = new Compiler(this.options.context);
this.compiler.options = this.options;
// 3. 注册内置插件
this.registerBuiltinPlugins();
// 4. 应用用户插件
this.applyPlugins();
}
processOptions(options) {
// 配置标准化和默认值处理
const defaultOptions = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(process.cwd(), 'dist'),
filename: '[name].js'
},
module: { rules: [] },
plugins: []
};
return { ...defaultOptions, ...options };
}
registerBuiltinPlugins() {
// 注册内置插件
new EntryOptionPlugin().apply(this.compiler);
new RuntimePlugin().apply(this.compiler);
new InferAsyncModulesPlugin().apply(this.compiler);
}
}
Webpack5 整体架构流程
核心概念关系
javascript
// Webpack5 核心概念及其关系
class WebpackConcepts {
// 入口:构建依赖图的起点
static Entry = {
single: './src/index.js',
multiple: {
app: './src/app.js',
vendor: './src/vendor.js'
},
dynamic: () => './src/dynamic-entry.js'
};
// 输出:打包后的文件配置
static Output = {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
chunkFilename: '[name].[chunkhash].chunk.js',
publicPath: '/',
library: 'MyLibrary',
libraryTarget: 'umd'
};
// 模块:Webpack 中一切皆模块
static Module = {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
};
// 插件:扩展 Webpack 功能
static Plugins = [
new HtmlWebpackPlugin(),
new MiniCssExtractPlugin(),
new CleanWebpackPlugin()
];
}
核心源码
Compiler
Compiler 是 Webpack 的核心控制器,负责整个编译过程的生命周期管理。它继承自 Tapable,提供了丰富的钩子系统。
javascript
// lib/Compiler.js 核心实现
const { AsyncSeriesHook, SyncHook, AsyncParallelHook } = require('tapable');
class Compiler extends Tapable {
constructor(context) {
super();
this.context = context;
this.hooks = {
// 编译开始前
beforeRun: new AsyncSeriesHook(['compiler']),
// 编译开始
run: new AsyncSeriesHook(['compiler']),
// 编译完成
done: new AsyncSeriesHook(['stats']),
// 编译失败
failed: new SyncHook(['error']),
// 监听模式下文件变化
watchRun: new AsyncSeriesHook(['compiler']),
// 创建 compilation 前
beforeCompile: new AsyncSeriesHook(['params']),
// 创建 compilation
compile: new SyncHook(['params']),
// compilation 创建完成
thisCompilation: new SyncHook(['compilation', 'params']),
// compilation 完成
compilation: new SyncHook(['compilation', 'params']),
// 生成资源
emit: new AsyncSeriesHook(['compilation']),
// 资源生成完成
afterEmit: new AsyncSeriesHook(['compilation'])
};
this.running = false;
this.watchMode = false;
this.fileSystemInfo = new FileSystemInfo();
}
// 运行编译
run(callback) {
if (this.running) {
return callback(new Error('Compiler is already running'));
}
this.running = true;
const onCompiled = (err, compilation) => {
if (err) return callback(err);
if (this.hooks.shouldEmit.call(compilation) === false) {
const stats = new Stats(compilation);
this.hooks.done.callAsync(stats, () => {
this.running = false;
callback(null, stats);
});
return;
}
this.emitAssets(compilation, err => {
if (err) return callback(err);
this.hooks.afterEmit.callAsync(compilation, err => {
if (err) return callback(err);
const stats = new Stats(compilation);
this.hooks.done.callAsync(stats, () => {
this.running = false;
callback(null, stats);
});
});
});
};
this.hooks.beforeRun.callAsync(this, err => {
if (err) return callback(err);
this.hooks.run.callAsync(this, err => {
if (err) return callback(err);
this.compile(onCompiled);
});
});
}
// 核心编译方法
compile(callback) {
const params = this.newCompilationParams();
this.hooks.beforeCompile.callAsync(params, err => {
if (err) return callback(err);
this.hooks.compile.call(params);
const compilation = this.newCompilation(params);
this.hooks.make.callAsync(compilation, err => {
if (err) return callback(err);
compilation.finish(err => {
if (err) return callback(err);
compilation.seal(err => {
if (err) return callback(err);
this.hooks.afterCompile.callAsync(compilation, err => {
if (err) return callback(err);
callback(null, compilation);
});
});
});
});
});
}
}
Compiler 生命周期
Compilation
Compilation 负责具体的编译工作,每次编译都会创建一个新的 Compilation 实例。它管理着模块的构建、依赖解析和代码生成。
javascript
// lib/Compilation.js 核心实现
class Compilation extends Tapable {
constructor(compiler) {
super();
this.compiler = compiler;
this.hooks = {
// 构建模块
buildModule: new SyncHook(['module']),
// 重新构建模块
rebuildModule: new SyncHook(['module']),
// 模块构建失败
failedModule: new SyncHook(['module', 'error']),
// 模块构建成功
succeedModule: new SyncHook(['module']),
// 添加入口
addEntry: new SyncHook(['entry', 'options']),
// 依赖引用
dependencyReference: new SyncWaterfallHook(['dependencyReference', 'dependency', 'module']),
// 优化模块
optimizeModules: new SyncBailHook(['modules']),
// 优化块
optimizeChunks: new SyncBailHook(['chunks', 'chunkGroups']),
// 优化树
optimizeTree: new AsyncSeriesHook(['chunks', 'modules']),
// 额外资源处理
additionalAssets: new AsyncSeriesHook([]),
// 创建模块资源
createModuleAssets: new SyncHook([]),
// 优化块资源
optimizeChunkAssets: new AsyncSeriesHook(['chunks']),
// 优化资源
optimizeAssets: new AsyncSeriesHook(['assets'])
};
this.modules = new Set();
this.chunks = new Set();
this.assets = {};
this.dependencyFactories = new Map();
this.dependencyTemplates = new Map();
}
// 添加入口模块
addEntry(context, dependency, options, callback) {
const slot = {
name: options.name,
includeDependencies: [dependency],
options
};
this.hooks.addEntry.call(dependency, options);
this._addModuleChain(context, dependency, (module) => {
this.entries.set(options.name, module);
}, callback);
}
// 构建模块链
_addModuleChain(context, dependency, onModule, callback) {
const moduleFactory = this.dependencyFactories.get(dependency.constructor);
moduleFactory.create({
context,
dependencies: [dependency],
contextInfo: { issuer: '' }
}, (err, module) => {
if (err) return callback(err);
this.buildModule(module, (err) => {
if (err) return callback(err);
onModule(module);
this.processModuleDependencies(module, callback);
});
});
}
// 构建模块
buildModule(module, callback) {
this.hooks.buildModule.call(module);
module.build(this.options, this, this.resolverFactory.get('normal'), this.inputFileSystem, (err) => {
if (err) {
this.hooks.failedModule.call(module, err);
return callback(err);
}
this.hooks.succeedModule.call(module);
callback();
});
}
// 处理模块依赖
processModuleDependencies(module, callback) {
const dependencies = [];
// 收集所有依赖
module.dependencies.forEach(dependency => {
dependencies.push({
factory: this.dependencyFactories.get(dependency.constructor),
dependency,
originModule: module
});
});
// 并行处理依赖
async.forEach(dependencies, (item, callback) => {
this.handleModuleCreation(item, callback);
}, callback);
}
// 封装构建结果
seal(callback) {
this.hooks.seal.call();
// 创建块
this.createChunks();
// 优化
this.optimize();
// 生成代码
this.createModuleAssets();
this.hooks.additionalAssets.callAsync(err => {
if (err) return callback(err);
this.summarizeDependencies();
this.createChunkAssets();
callback();
});
}
// 创建块
createChunks() {
const chunkGraph = new ChunkGraph();
for (const [name, module] of this.entries) {
const chunk = this.addChunk(name);
const entrypoint = new Entrypoint(name);
entrypoint.setRuntimeChunk(chunk);
entrypoint.addOrigin(null, name, module.request);
chunkGraph.connectChunkAndEntryModule(chunk, module, entrypoint);
}
this.chunkGraph = chunkGraph;
}
}
Compilation 工作流程
Module
Module 是 Webpack 中的基本单位,代表一个模块文件。所有类型的文件都会被转换为 Module 对象。
javascript
// lib/Module.js 核心实现
class Module extends DependenciesBlock {
constructor(type, context) {
super();
this.type = type;
this.context = context;
this.debugId = debugId++;
this.hash = undefined;
this.renderedHash = undefined;
this.resolveOptions = EMPTY_RESOLVE_OPTIONS;
this.factoryMeta = {};
this.warnings = [];
this.errors = [];
this.buildMeta = {};
this.buildInfo = {};
this.presentationalDependencies = [];
}
// 模块标识符
identifier() {
throw new Error('Module.identifier() must be implemented');
}
// 可读的标识符
readableIdentifier(requestShortener) {
return requestShortener.shorten(this.identifier());
}
// 构建模块
build(options, compilation, resolver, fs, callback) {
this.buildTimestamp = Date.now();
this.built = true;
this._source = null;
this._buildHash = '';
this.buildMeta = {};
this.buildInfo = {
cacheable: true,
parsed: true,
fileDependencies: new LazySet(),
contextDependencies: new LazySet(),
missingDependencies: new LazySet(),
buildDependencies: new LazySet(),
assets: undefined,
assetsInfo: undefined
};
return this.doBuild(options, compilation, resolver, fs, err => {
if (err) return callback(err);
const handleParseError = e => {
const source = this._source.source();
const error = new ModuleParseError(source, e);
this.markModuleAsErrored(error);
this._initBuildHash(compilation);
return callback(error);
};
const handleParseResult = result => {
this.dependencies.length = 0;
this.blocks.length = 0;
this.variables.length = 0;
this.buildMeta = result.buildMeta || {};
this.buildInfo = result.buildInfo || {};
this._initBuildHash(compilation);
return callback();
};
try {
const result = this.parser.parse(this._ast || this._source.source(), {
current: this,
module: this,
compilation: compilation,
options: options
});
handleParseResult(result);
} catch (e) {
handleParseError(e);
}
});
}
// 实际构建逻辑
doBuild(options, compilation, resolver, fs, callback) {
throw new Error('Module.doBuild() must be implemented');
}
// 获取源码
source(dependencyTemplates, runtimeTemplate, type = 'javascript') {
const source = this.generator.generate(this, {
dependencyTemplates,
runtimeTemplate,
type
});
return source;
}
// 计算哈希
updateHash(hash, chunkGraph, runtime) {
hash.update(this.buildMeta.exportsType || '');
hash.update(`${this.buildMeta.strictHarmonyModule}`);
hash.update(`${this.buildMeta.exportsArgument}`);
hash.update(`${this.buildMeta.async}`);
super.updateHash(hash, chunkGraph, runtime);
}
}
// NormalModule 继承自 Module,处理普通的 JS 模块
class NormalModule extends Module {
constructor({
type,
request,
userRequest,
rawRequest,
loaders,
resource,
parser,
generator,
resolveOptions
}) {
super(type, getContext(resource));
this.request = request;
this.userRequest = userRequest;
this.rawRequest = rawRequest;
this.loaders = loaders;
this.resource = resource;
this.parser = parser;
this.generator = generator;
this.resolveOptions = resolveOptions;
}
// 实际构建
doBuild(options, compilation, resolver, fs, callback) {
const loaderContext = this.createLoaderContext(resolver, options, compilation, fs);
runLoaders({
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: fs.readFile.bind(fs)
}, (err, result) => {
if (err) return callback(err);
this._source = this.createSource(options.context, result.result[0], result.map);
this._sourceSize = null;
this._ast = result.result.length >= 1 ? result.result[1] : null;
callback();
});
}
// 创建源码对象
createSource(context, content, sourceMap) {
if (Buffer.isBuffer(content)) {
return new RawSource(content);
}
if (!sourceMap) {
return new RawSource(content);
}
return new SourceMapSource(content, this.identifier(), sourceMap);
}
}
NormalModule 构建流程
Dependency
Dependency 表示模块间的依赖关系,Webpack 通过解析这些依赖来构建完整的依赖图。
javascript
// lib/Dependency.js 核心实现
class Dependency {
constructor() {
this._parentModule = undefined;
this._parentDependenciesBlock = undefined;
this.weak = false;
this.optional = false;
}
// 依赖类型
getResourceIdentifier() {
return null;
}
// 获取引用的模块
getReference(moduleGraph) {
throw new Error('Dependency.getReference() must be implemented');
}
// 获取导出
getExports(moduleGraph) {
return undefined;
}
// 获取警告
getWarnings(moduleGraph) {
return null;
}
// 获取错误
getErrors(moduleGraph) {
return null;
}
// 序列化
serialize(context) {
const { write } = context;
write(this.weak);
write(this.optional);
}
// 反序列化
deserialize(context) {
const { read } = context;
this.weak = read();
this.optional = read();
}
}
// ModuleDependency 模块依赖基类
class ModuleDependency extends Dependency {
constructor(request) {
super();
this.request = request;
this.userRequest = request;
}
getResourceIdentifier() {
return `module${this.request}`;
}
createIgnoredModule(context) {
return new RawModule('/* (ignored) */', `ignored|${context}|${this.request}`, `${this.request} (ignored)`);
}
serialize(context) {
const { write } = context;
write(this.request);
write(this.userRequest);
super.serialize(context);
}
deserialize(context) {
const { read } = context;
this.request = read();
this.userRequest = read();
super.deserialize(context);
}
}
// CommonJS require 依赖
class CommonJsRequireDependency extends ModuleDependency {
constructor(request, range) {
super(request);
this.range = range;
}
get type() {
return 'cjs require';
}
getReference(moduleGraph) {
const module = moduleGraph.getModule(this);
if (!module) return null;
return {
module,
weak: this.weak,
importedNames: true
};
}
}
// ES6 import 依赖
class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
constructor(request, sourceOrder) {
super(request, sourceOrder);
}
get type() {
return 'harmony side effect evaluation';
}
getReference(moduleGraph) {
return {
module: moduleGraph.getModule(this),
weak: false,
importedNames: false
};
}
}
依赖解析流程
Loader
Loader 是 Webpack 的核心功能之一,负责将各种类型的文件转换为 JavaScript 模块。Loader 本质上是一个函数,接收源码作为输入,返回转换后的代码。
javascript
// Loader 的基本结构
function myLoader(source, map, meta) {
// source: 源文件内容
// map: 可以是 SourceMap 数据
// meta: meta 数据,可以是任何内容
// 获取 loader 的配置选项
const options = this.getOptions();
// 获取 callback 函数
const callback = this.async();
// 异步处理
setTimeout(() => {
// 进行代码转换
const result = transform(source, options);
// 调用 callback 返回结果
// callback(error, transformedSource, sourceMap, meta)
callback(null, result, map, meta);
}, 100);
}
// 处理 raw 内容
myLoader.raw = true;
module.exports = myLoader;
Loader 工作流程
Loader Context API
javascript
// Loader 上下文提供的 API
class LoaderContext {
constructor() {
// 当前处理的文件路径
this.resourcePath = '';
this.resourceQuery = '';
this.resourceFragment = '';
// 请求字符串
this.request = '';
this.userRequest = '';
this.rawRequest = '';
// Loader 配置
this.loaders = [];
this.loaderIndex = 0;
// 编译上下文
this.context = '';
this.rootContext = '';
// 文件系统相关
this.fs = null;
// 缓存相关
this.cacheable = true;
// 依赖收集
this.addDependency = (file) => {
this.dependencies.push(file);
};
this.addContextDependency = (context) => {
this.contextDependencies.push(context);
};
this.addMissingDependency = (missing) => {
this.missingDependencies.push(missing);
};
}
// 获取 loader 选项
getOptions(schema) {
const options = this.query;
if (schema && typeof options === 'object') {
validate(schema, options, {
name: 'Loader',
baseDataPath: 'options'
});
}
return options || {};
}
// 异步回调
async() {
if (this.callback) {
throw new Error('already called');
}
this.callback = this.callback || function() {};
return this.callback;
}
// 同步返回
return(content, sourceMap, meta) {
if (this.callback) {
this.callback(null, content, sourceMap, meta);
} else {
this.result = [content, sourceMap, meta];
}
}
// 缓存控制
cacheable(flag = true) {
this.cacheable = flag;
}
// 解析请求
resolve(context, request, callback) {
this.resolver.resolve({}, context, request, {}, callback);
}
// 加载模块
loadModule(request, callback) {
this.compilation.buildModule(
this.compilation.moduleGraph.getModule(this.dependency),
callback
);
}
}
常用 Loader 实现示例
javascript
// Babel Loader 简化实现
function babelLoader(source, inputSourceMap) {
const options = this.getOptions();
const callback = this.async();
// 如果没有配置,直接返回源码
if (!options.presets && !options.plugins) {
return callback(null, source, inputSourceMap);
}
// 使用 Babel 转换
babel.transform(source, {
...options,
filename: this.resourcePath,
inputSourceMap: inputSourceMap || undefined,
}, (err, result) => {
if (err) return callback(err);
callback(null, result.code, result.map);
});
}
// CSS Loader 简化实现
function cssLoader(source) {
const options = this.getOptions();
const callback = this.async();
// 解析 CSS
postcss([
require('postcss-import')(),
require('autoprefixer')()
])
.process(source, {
from: this.resourcePath,
map: { inline: false }
})
.then(result => {
// 转换为 JavaScript 模块
const jsCode = `
const css = ${JSON.stringify(result.css)};
export default css;
`;
callback(null, jsCode, result.map);
})
.catch(callback);
}
// File Loader 简化实现
function fileLoader(content) {
const options = this.getOptions();
const context = options.context || this.rootContext;
// 生成文件名
const url = interpolateName(this, options.name || '[hash].[ext]', {
context,
content,
regExp: options.regExp
});
// 输出文件
this.emitFile(url, content);
// 返回文件路径
return `module.exports = ${JSON.stringify(url)};`;
}
fileLoader.raw = true; // 处理二进制内容
Plugin
Plugin 是 Webpack 的另一个核心概念,通过监听编译过程中的事件钩子来扩展 Webpack 的功能。
javascript
// Plugin 的基本结构
class MyPlugin {
constructor(options = {}) {
this.options = options;
}
// apply 方法是插件的入口
apply(compiler) {
const pluginName = MyPlugin.name;
// 监听 compilation 钩子
compiler.hooks.compilation.tap(pluginName, (compilation) => {
// 监听 Compilation 的钩子
compilation.hooks.optimizeAssets.tapAsync(pluginName, (assets, callback) => {
// 处理资源
for (const assetName in assets) {
const asset = assets[assetName];
const source = asset.source();
// 进行一些处理
const processedSource = this.processAsset(source);
// 更新资源
assets[assetName] = {
source: () => processedSource,
size: () => processedSource.length
};
}
callback();
});
});
// 监听其他钩子
compiler.hooks.emit.tapAsync(pluginName, (compilation, callback) => {
// 在输出资源前执行
console.log('Assets will be emitted:', Object.keys(compilation.assets));
callback();
});
}
processAsset(source) {
// 处理资源的逻辑
return source.replace(/console\.log/g, '// console.log');
}
}
module.exports = MyPlugin;
Plugin 工作流程
常用 Plugin 实现示例
javascript
// HTML Webpack Plugin 简化实现
class HtmlWebpackPlugin {
constructor(options = {}) {
this.options = {
template: 'src/index.html',
filename: 'index.html',
inject: true,
...options
};
}
apply(compiler) {
const pluginName = HtmlWebpackPlugin.name;
compiler.hooks.emit.tapAsync(pluginName, (compilation, callback) => {
// 获取入口 chunks
const entryNames = Array.from(compilation.entrypoints.keys());
const assets = this.getAssets(compilation, entryNames);
// 读取模板
const templateContent = compilation.inputFileSystem.readFileSync(
this.options.template,
'utf8'
);
// 生成 HTML
const html = this.generateHtml(templateContent, assets);
// 添加到输出资源
compilation.assets[this.options.filename] = {
source: () => html,
size: () => html.length
};
callback();
});
}
getAssets(compilation, entryNames) {
const assets = { js: [], css: [] };
entryNames.forEach(entryName => {
const entrypoint = compilation.entrypoints.get(entryName);
const chunks = entrypoint.getFiles();
chunks.forEach(chunk => {
if (chunk.endsWith('.js')) {
assets.js.push(chunk);
} else if (chunk.endsWith('.css')) {
assets.css.push(chunk);
}
});
});
return assets;
}
generateHtml(template, assets) {
let html = template;
// 注入 CSS
const cssLinks = assets.css.map(css =>
`<link rel="stylesheet" href="${css}">`
).join('\n');
// 注入 JS
const jsScripts = assets.js.map(js =>
`<script src="${js}"></script>`
).join('\n');
// 替换占位符
html = html.replace('</head>', `${cssLinks}\n</head>`);
html = html.replace('</body>', `${jsScripts}\n</body>`);
return html;
}
}
// Mini CSS Extract Plugin 简化实现
class MiniCssExtractPlugin {
constructor(options = {}) {
this.options = {
filename: '[name].css',
chunkFilename: '[id].css',
...options
};
}
apply(compiler) {
const pluginName = MiniCssExtractPlugin.name;
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
compilation.hooks.processAssets.tap({
name: pluginName,
stage: compilation.PROCESS_ASSETS_STAGE_EXTRACT
}, () => {
const chunks = compilation.chunks;
chunks.forEach(chunk => {
const cssModules = [];
// 收集 CSS 模块
compilation.chunkGraph.getChunkModulesIterable(chunk).forEach(module => {
if (module.type === 'css/mini-extract') {
cssModules.push(module);
}
});
if (cssModules.length > 0) {
// 合并 CSS 内容
const cssContent = cssModules.map(m => m.content).join('\n');
// 生成文件名
const filename = compilation.getPath(this.options.filename, {
chunk,
contentHash: this.getContentHash(cssContent)
});
// 添加到资源
compilation.assets[filename] = {
source: () => cssContent,
size: () => cssContent.length
};
// 更新 chunk 文件列表
chunk.files.add(filename);
}
});
});
});
}
getContentHash(content) {
return crypto.createHash('md5').update(content).digest('hex').slice(0, 8);
}
}
Loader 与 Plugin 执行过程分析
Loader 的执行
Loader 的执行是在模块构建阶段,按照从右到左、从下到上的顺序执行。
javascript
// Loader 执行器实现
class LoaderRunner {
constructor(options) {
this.resource = options.resource;
this.loaders = options.loaders;
this.context = options.context;
this.readResource = options.readResource;
}
run(callback) {
let loaderIndex = this.loaders.length - 1;
let resourceBuffer = null;
// 创建 loader 上下文
const loaderContext = this.createLoaderContext();
// 递归执行 loader
const iterateLoaders = (options, callback) => {
if (loaderIndex < 0) {
// 所有 loader 执行完毕,读取文件
return this.readResource(this.resource, (err, buffer) => {
if (err) return callback(err);
resourceBuffer = buffer;
loaderIndex = this.loaders.length - 1;
iterateNormalLoaders(options, callback);
});
}
const currentLoader = this.loaders[loaderIndex--];
// 执行 loader
this.runSyncOrAsync(currentLoader, loaderContext, [resourceBuffer], (err, ...results) => {
if (err) return callback(err);
resourceBuffer = results[0];
iterateLoaders(options, callback);
});
};
// 开始执行
iterateLoaders({}, callback);
}
createLoaderContext() {
const context = {
version: 2,
resource: this.resource,
resourcePath: this.resource.split('?')[0],
resourceQuery: this.resource.includes('?') ? this.resource.split('?')[1] : '',
async: () => {
const callback = context.callback;
context.callback = null;
return callback;
},
getOptions: (schema) => {
const loader = this.loaders[loaderIndex];
return loader.options || {};
},
emitFile: (name, content, sourceMap) => {
this.compilation.assets[name] = {
source: () => content,
size: () => content.length
};
}
};
return context;
}
runSyncOrAsync(loader, context, args, callback) {
let isSync = true;
let isDone = false;
// 设置异步回调
context.callback = (...results) => {
if (isDone) return;
isDone = true;
callback(null, ...results);
};
// 执行 loader 函数
try {
const result = loader.apply(context, args);
if (isSync) {
isDone = true;
if (result === undefined) {
return callback();
}
return callback(null, result);
}
} catch (err) {
if (isDone) return;
isDone = true;
return callback(err);
}
isSync = false;
}
}
Loader 执行流程
Plugin 的执行
Plugin 通过监听 Webpack 的生命周期钩子来执行,可以在编译的各个阶段介入处理。
javascript
// Plugin 执行过程分析
class PluginExecutor {
constructor(compiler) {
this.compiler = compiler;
this.plugins = [];
}
// 应用插件
applyPlugins(plugins) {
plugins.forEach(plugin => {
if (typeof plugin === 'function') {
plugin.call(this.compiler, this.compiler);
} else {
plugin.apply(this.compiler);
}
});
}
// 触发钩子
triggerHook(hookName, ...args) {
const hook = this.compiler.hooks[hookName];
if (!hook) return;
switch (hook.constructor.name) {
case 'SyncHook':
hook.call(...args);
break;
case 'AsyncSeriesHook':
hook.callAsync(...args, (err) => {
if (err) throw err;
});
break;
case 'AsyncParallelHook':
hook.callAsync(...args, (err) => {
if (err) throw err;
});
break;
case 'SyncWaterfallHook':
return hook.call(...args);
case 'AsyncSeriesWaterfallHook':
hook.callAsync(...args, (err, result) => {
if (err) throw err;
return result;
});
break;
}
}
}
Plugin 执行流程
完整的编译流程总览
通过以上深入的源码分析,我们可以看到 Webpack5 的强大之处在于其灵活的插件架构和完善的生命周期钩子系统。Compiler 负责整体的编译流程控制,Compilation 处理具体的编译任务,Module 和 Dependency 构成了依赖图的基础,而 Loader 和 Plugin 则提供了强大的扩展能力。
理解这些核心概念和执行流程,对于前端工程师深度定制构建流程、性能优化以及问题排查都具有重要意义。在实际项目中,我们可以基于这些原理来开发自定义的 Loader 和 Plugin,以满足特定的业务需求。