current position:Home>Webpack5 packaging process source code analysis (1)
Webpack5 packaging process source code analysis (1)
2022-08-06 19:02:33【Mingli people】
开篇
webpack 相信大家都不陌生,In recent years is widely used in packaging resources of front-end engineering.
通常,我们只要掌握了 webpack 一些常用配置,Enough to meet the project build most scenarios.
然,When do you want to go to stand in a higher perspective to look at and use webpack 时,Like to do optimization、自定义 plugin 和 loader,理解 webpack Packaging of the compilation process is particularly important.Master packaging process online at various stages of the work done by,More accurate can help us to achieve high standards of customized function.
本篇,我们将以 Webpack 5 作为材料,Debugging the source code to get familiar with webpack Packaging process line,A source on the process are familiar with,In the future to further the principle of that part of the configuration will be easier.
一、前置知识
「1. Webpack」
webpack 是 JavaScript 应用程序静态模块打包器.Its source design adopted plug-in architecture,由 Tapable
The ability to provide registration and call the plugin.因此,webpack The application of a lot of plug-ins,To complete for each config The realization of the function of configuration items.
「2. Compiler」
compiler 理解为 编译器,仅在 webpack Initialization time to create an instance,是 webpack Packaging process on the backbone of the engine.
在 compiler Instance provides a number of hooks
To user-oriented realization of custom plug-ins,在 run
After start the packaging will be created compilation
Instance processing module compilation.
「3. Compilation」compilation
理解为 编译,是由 compiler
The compiler to create a,A compiler to compile may create one or more,则 compilation
可能会被创建多次.(watch
)
compilation The entry module will start,Compile the module and its dependence on child module.All modules compiled will pass:加载(loaded)、封存(sealed)、优化(optimized)、分块(chunked)、哈希(hashed) 和 重新创建(restored).
「4. Dependence」
webpack For it to record module dependencies between.在模块中引用其它模块,Will reference relationship expressed as Dependency 子类并关联 module 对象,等到当前 module 内容都解析完毕之后,启动下次循环开始将 Dependency Object is transformed to a new Module 子类.
「5. Module」
webpack When handling each resource file,都会以 module 对象形式存在,包含了资源的路径、上下文、依赖、内容等信息.All the resources to build、转译、Merger is also module 为基本单位进行.
「6. Chunk」
编译完成准备输出时,webpack 会将 module 按特定的规则组织成一个一个的 chunk,这些 chunk To some extent with the final output file one-to-one correspondence.
「7. Assets」
asset Represents the final output to disk file content,它与 chunk 一一对应.
「8. Tapable」
Tapable The ability to provide registration and call the plugin,来串联 webapck The whole packaging process of plugins work.
It can trigger hooks at the particular time,With enough context information on,Go to the notification plug-in to register hook callback,去产生 side effect
Compiling state and subsequent process.
流程概览
webpack Packaging process as a whole can be divided into four large,A subsequent source debugging analysis will also be in accordance with the following division.
- 初始化阶段
- 构建阶段(make)
- 生成阶段(seal)
- 写入阶段(emit)
Because the content and length is too long,Will be divided into two articles to introduce process,本篇主要介绍「初始化阶段」和 「构建阶段」;「生成阶段」和「写入阶段」请移步到 webpack5 Packaging process source code analysis(2).
二、调试环境
Let's initialize a debugging environment:
mkdir webpack-debugger && cd webpack-debugger && npm init -y && npm install webpack webpack-cli -D
复制代码
在 webpack-debugger
目录下创建 src/index.js
作为入口模块,Add a line of print code file:
// src/index.js
conspole.log('webpack-debugger');
复制代码
新建 webpack.config.js
,We use the most simple packaging configuration:
// webpack.config.js
const path = require('path');
module.exports = () => ({
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'static/js/[name].[contenthash:8].js',
}
})
复制代码
最后,创建 build.js
作为调用 webpack 打包的执行文件:
// build.js
const path = require('path');
const webpack = require('webpack');
const configFactory = require('./webpack.config.js');
const config = configFactory();
debugger;
const compiler = webpack(config);
debugger;
compiler.run();
复制代码
通常我们都会在 package.json scripts 字段中使用 webpack bin Command to webpack-cli
进行打包,而进入 webpack-cli Inside the package with the above the same.
上述代码可知:webpack The default export a function method,接收 config A configuration object as a parameter,返回 compiler
对象;通过 compiler.run()
Open the packaging process.
如果你使用的 VSCode,新建一个 JavaScript Debug Terminal
并执行 node build.js
开启调试.
Next, we analysis the initialization phase webpack 做了哪些事情.
三、初始化阶段
Before we put in a real building entrance module of this phase is divided into初始化阶段,主要步骤如下:
- 初始化参数:The user into the configured with the default configuration of final configuration parameter;
- 创建编译器对象:According to the configuration parameter to create
Compiler
实例对象; - 初始化编译环境:Registered users to configure the plug-in, and plug-ins;
- 运行编译:执行
compiler.run
方法; - 确定入口:根据配置
entry
Looking for all entry documents,并转换为dependence
对象,等待执行compilition.addEntry
编译工作.
3.1、webpack()
webpack Depend on the package default derived a method,这个方法的定义在 webpack/lib/webpack.js
之中:
// webpack/lib/webpack.js
const webpack = (options, callback) => {
const create = () => {
const webpackOptions = options;
const compiler = createCompiler(webpackOptions);
}
if (callback) {
const { compiler } = create();
compiler.run((err, stats) => {
compiler.close(err2 => {
callback(err || err2, stats);
});
});
return compiler;
} else {
const { compiler } = create();
return compiler;
}
}
复制代码
This method allows two parameters,若传递了 callback
,创建 compiler Instance automatically after the call run 方法启动打包,Otherwise to external manual calls to start packing.
3.2、createCompiler
参数合并、compiler
The compiler instance creation、Registration of external and internal plug-ins are all here to do:
// webpack/lib/webpack.js
const createCompiler = rawOptions => {
// 规范 webpack config 配置项(创建 config 配置项)
const options = getNormalizedWebpackOptions(rawOptions);
// 设置默认的 config.context
applyWebpackOptionsBaseDefaults(options);
// 创建 compiler 编译器实例
const compiler = new Compiler(options.context, options);
// 应用 Node 环境插件,如为 compiler 提供 fs 文件操作 API(fs Module secondary packaging).
new NodeEnvironmentPlugin({
infrastructureLogging: options.infrastructureLogging
}).apply(compiler);
// Registered users of the incoming plug-in configuration
if (Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
// Application configuration items default value
applyWebpackOptionsDefaults(options);
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
// 关键,注册 Webpack Packaging process of plug-ins
new WebpackOptionsApply().process(options, compiler);
compiler.hooks.initialize.call();
return compiler;
};
复制代码
Here we focus on to mention two:
- new Compiler(options.context, options) Compiler 是一个 ES6 class 构造函数,Here we just learned that one run 方法,Its basic structure is as follows:
// webpack/lib/Compiler.js
class Compiler {
constructor(context, options = {}) {
this.hooks = Object.freeze({
initialize: new SyncHook([]),
run: new AsyncSeriesHook(["compiler"]),
done: new AsyncSeriesHook(["stats"]),
emit: new AsyncSeriesHook(["compilation"]),
make: new AsyncParallelHook(["compilation"]),
... 很多很多
});
this.options = options;
this.context = context;
}
run(callback) {}
}
复制代码
- new WebpackOptionsApply().process(options, compiler) 上面「前置知识」中我们了解到:webpack Is a plugin structured design architecture,Namely the realization of each function is done by a plug-in to the,Such as modules compiled entrance
entry
是由EntryPlugin
来管理和执行.
WebpackOptionsApply().process
Registered plug-ins a lot,Here we only care about this will involve the plug-in configuration.
// webpack/lib/WebpackOptionsApply.js
class WebpackOptionsApply extends OptionsApply {
constructor() {
super();
}
process(options, compiler) {
new JavascriptModulesPlugin().apply(compiler);
// entry 插件
new EntryOptionPlugin().apply(compiler);
compiler.hooks.entryOption.call(options.context, options.entry);
...
}
}
复制代码
EntryOptionPlugin
会为config.entry
Each of the configuration applicationEntryPlugin
To register the compilation of entry,等后续hooks.make
Time to start the entry module compiler.JavascriptModulesPlugin
提供了parse
AST 的核心实现,Subsequent collection module are introduceddeps
Rely on the module will use.
到这里,compiler 实例创建完成,And the related plug-in registration.
接下来会执行 compiler.run()
Open the package.Due to haven't go to the real compiler,Put this part「初始化阶段」一并介绍.
3.3、compiler.run
// webpack/lib/Compiler.js
class Compiler {
...
run(callback) {
const finalCallback = (err, stats) => {} // All work is completed the final executive function
const onCompiled = (err, compilation) => {} // Compiled after the completion of the execution of the function
this.hooks.beforeRun.callAsync(this, err => {
if (err) return finalCallback(err);
this.hooks.run.callAsync(this, err => {
if (err) return finalCallback(err);
this.compile(onCompiled);
});
});
}
}
复制代码
首先执行了 beforeRun
和 run
两个 hook 钩子,If there is a plug-in registered these two types of hooks,Register the callback function will be executed immediately.Here we go this.compile
之中.
// webpack/lib/Compiler.js
class Compiler {
...
compile(callback) {
const params = this.newCompilationParams();
this.hooks.beforeCompile.callAsync(params, err => {
this.hooks.compile.call(params);
const compilation = this.newCompilation(params);
this.hooks.make.callAsync(compilation, err => {
compilation.finish(err => {
compilation.seal(err => {
return callback(null, compilation);
});
});
});
});
}
}
复制代码
compile()
Is the key to start the compilation,编译实例 compilation
的参数定义、Instance creation and compiled after the completion of the finishing touches are all here to realize.
- 首先是
this.newCompilationParams
创建 compilation Compile the module parameters needed:
// webpack/lib/Compiler.js
newCompilationParams() {
const params = {
normalModuleFactory: this.createNormalModuleFactory(),
contextModuleFactory: this.createContextModuleFactory()
};
return params;
}
复制代码
这里,我们需要留意一下 createNormalModuleFactory
,在 webpack 中,Each dependent modules can be seen as a Module
对象,Usually there will be a lot of modules to deal with,Create a module factory here Factory.
// webpack/lib/Compiler.js
createNormalModuleFactory() {
const normalModuleFactory = new NormalModuleFactory({
context: this.options.context,
fs: this.inputFileSystem,
resolverFactory: this.resolverFactory, // resolve 模块时使用
options: this.options.module,
associatedObjectForCache: this.root,
layers: this.options.experiments.layers
});
this._lastNormalModuleFactory = normalModuleFactory;
this.hooks.normalModuleFactory.call(normalModuleFactory);
return normalModuleFactory;
}
复制代码
- 创建
compilation
实例对象:
// webpack/lib/Compiler.js
newCompilation(params) {
this._cleanupLastCompilation(); // 清除上次 compilation
const compilation = this._lastCompilation = new Compilation(this, params);
this.hooks.thisCompilation.call(compilation, params);
this.hooks.compilation.call(compilation, params);
return compilation;
}
复制代码
Compilation
和 Compiler
都是一个 class 构造函数,Instance also contains a lot of properties and methods.
// webpack/lib/Compilation.js
class Compilation {
constructor(compiler, params) {
this.hooks = Object.freeze({ ... });
this.compiler = compiler;
this.params = params;
this.options = compiler.options;
this.entries = new Map(); // 存储 entry module
this.modules = new Set();
this._modules = new Map(); // 存储所有 module
...
}
}
复制代码
- 调用
hooks.make
这一步很关键,In the above create getcompilation
之后,To get into compilation phase,Will compile from the entrance to the module to begin.
While the module of preparation is registered in EntryOptionPlugin
之中:
// webpack/lib/EntryOptionsPlugin.js
class EntryOptionPlugin {
apply(compiler) {
compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
EntryOptionPlugin.applyEntryOption(compiler, context, entry);
return true;
});
}
static applyEntryOption(compiler, context, entry) {
const EntryPlugin = require("./EntryPlugin");
for (const name of Object.keys(entry)) {
const desc = entry[name];
const options = EntryOptionPlugin.entryDescriptionToOptions(compiler, name, desc);
for (const entry of desc.import) {
new EntryPlugin(context, entry, options).apply(compiler); // 注册 entry 插件
}
}
}
}
复制代码
Which are a key part for each entry 注册 EntryPlugin
,在 EntryPlugin You will see and hook.make
相关的逻辑:
// webpack/lib/EntryPlugin.js
class EntryPlugin {
apply(compiler) {
// 1、记录 entry Parsing module is used when normalModuleFactory
compiler.hooks.compilation.tap(
"EntryPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
EntryDependency, // key
normalModuleFactory // value
);
}
);
const { entry, options, context } = this;
// 2、为 entry 创建 Dependency 对象
const dep = EntryPlugin.createDependency(entry, options);
// 3、监听 hook.make,执行 compilation.addEntry
compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
compilation.addEntry(context, dep, options, err => {
callback(err);
});
});
}
}
复制代码
现在我们清楚了:当执行 hooks.make.callAsync
时,其实就是执行 compilation.addEntry
Start the entry module compiler construction phase.
这也是很关键的一步:Find the entrance to the building.
四、构建阶段
hooks.make
Is the beginning of the trigger module compiler entrance,在 webpack 的构建阶段,流程如下:
- 为
entry
Entry module to create entryData 存储在compilation.entries
,For the follow-up for each entrance outputchunk
; - 拿到处理
entry
模块的工厂方法moduleFactory
,开始ModuleTree
的创建,Mr. Behind every file module into aModule
; - 执行
handleModuleCreation
Start dealing with the entry module build,当然,Introduced by entry module relies on module,Build will begin from here; - The build process will experience
feactorize(创建 module)
、addModule
、buildModule
三个阶段,build Phase involves loader Code conversion and rely on collection; - 模块构建完成后,If any child depend on(
module.dependencies
),Back to the third son began to rely on to build.
下面,Let's take a look at the process line in the code.
4.1、构建 EntryModuleTree
从 compilation.addEntry
开始进入 entry 模块的编译,调用 _addEntryItem
创建 entryData
加入到 this.entries
集合中.
// webpack/lib/Compilation.js
class Compilation {
constructor(compiler, params) {
this.entries = new Map();
...
}
addEntry(context, entry, options, callback) {
this._addEntryItem(context, entry, "dependencies", options, callback);
}
_addEntryItem(context, entry, target, options, callback) {
const { name } = options;
let entryData = name !== undefined ? this.entries.get(name) : this.globalEntry;
if (entryData === undefined) {
entryData = {
dependencies: [],
includeDependencies: [],
options: {
name: undefined,
...options
}
};
entryData[target].push(entry);
this.entries.set(name, entryData);
}
this.hooks.addEntry.call(entry, options);
this.addModuleTree({ context, dependency: entry, contextInfo: undefined }, (err, module) => {
this.hooks.succeedEntry.call(entry, options, module);
return callback(null, module);
});
}
}
复制代码
接着,执行 addModuleTree
获取 moduleFactory
The above storage normalModuleFactory
:
// webpack/lib/Compilation.js
addModuleTree({ context, dependency, contextInfo }, callback) {
const Dep = dependency.constructor; // EntryDependency
// dependencyFactories.get(EntryDependency) = normalModuleFactory
const moduleFactory = this.dependencyFactories.get(Dep); // 用于后续执行 moduleFactory.create()
this.handleModuleCreation({
factory: moduleFactory,
dependencies: [dependency],
originModule: null, contextInfo, context
}, (err, result) => {
callback(null, result);
});
}
复制代码
然后,执行 handleModuleCreation
:
// webpack/lib/Compilation.js
handleModuleCreation( { factory, // moduleFactory dependencies, // [dep] ... }, callback ) {
const moduleGraph = this.moduleGraph;
this.factorizeModule(
{
currentProfile: false,
factory,
dependencies,
factoryResult: true,
originModule,
contextInfo,
context
},
(err, factoryResult) => {
const newModule = factoryResult.module;
this.addModule(newModule, (err, module) => {
...
});
}
);
}
复制代码
factorizeModule
There is the meaning of decomposition module,可以理解为:为 entry 创建一个 Module
.Its logic function body is simple:
// webpack/lib/Compilation.js
Compilation.prototype.factorizeModule = function (options, callback) {
this.factorizeQueue.add(options, callback);
}
复制代码
4.2、Module compilation of phase
See here will be confused.从代码来看,将 options 加入到 factorizeQueue
In the process is over.
其实不然,这是一个 AsyncQueue
异步队列,You can understand for each module factorize Decomposition is a task to join in the queue,In a row to it would perform.
与 factorizeQueue
Similar to the function of the queue and addModuleQueue
和 buildQueue
,They are on the initialization compilation
Instance is defined as follows:
// webpack/lib/Compilation.js
class Compilation {
constructor(compiler, params) {
this.processDependenciesQueue = new AsyncQueue({
name: "processDependencies",
parallelism: options.parallelism || 100,
processor: this._processModuleDependencies.bind(this)
});
this.addModuleQueue = new AsyncQueue({
name: "addModule",
parent: this.processDependenciesQueue,
getKey: module => module.identifier(),
processor: this._addModule.bind(this)
});
this.factorizeQueue = new AsyncQueue({
name: "factorize",
parent: this.addModuleQueue,
processor: this._factorizeModule.bind(this)
});
this.buildQueue = new AsyncQueue({
name: "build",
parent: this.factorizeQueue,
processor: this._buildModule.bind(this)
});
}
_processModuleDependencies(module, callback) { }
_addModule(module, callback) { }
_factorizeModule(params, callback) { }
_buildModule(module, callback) { }
}
复制代码
A module compiler will pass factorize 创建模块
、addModule 添加模块
、buildQueue 构建模块
和 processDependencies Recursive handle child dependent modules(如果有)
几个阶段.
Each phase and the real execution of the function in Queue.processor
处理器上.
4.3、factorize 创建模块
_factorizeModule
The link is longer,先后经过:factory.create
--> hooks.factorize
--> hooks.resolve
--> new NormalModule
得到 module 对象.
// webpack/lib/Compilation.js
_factorizeModule( { currentProfile, factory, dependencies, originModule, factoryResult, contextInfo, context }, callback ) {
factory.create({ context, dependencies, ... }, (err, result) => {
callback(null, factoryResult ? result : result.module);
});
}
复制代码
factory.create
Is to create the module module
的开始,这里的 factory 就是创建 compilation.params 时传入的 normalModuleFactory
.
在 create()
中执行 hooks.factorize.callSync
,而注册 hooks.factorize.tapAsync
发生在初始化 NormalModuleFactory
实例时.
此外,Return when the initialization register hooks.resolve.tapAsync
,Its execution time better in hooks.factorize.tapAsync
之中.代码如下:
// webpack/lib/NormalModuleFactory.js
class NormalModuleFactory extends ModuleFactory {
constructor() {
this.hooks.factorize.tapAsync({}, (resolveData, callback) => {
this.hooks.resolve.callAsync(resolveData, (err, result) => {
...
}
})
this.hooks.resolve.tapAsync({}, (data, callback) => {
...
})
}
create(data, callback) {
const resolveData = {
contextInfo,
resolveOptions,
context,
request,
dependencies,
dependencyType,
createData: {},
cacheable: true
...
};
this.hooks.factorize.callAsync(resolveData, (err, module) => {
const factoryResult = {
module,
fileDependencies,
missingDependencies,
contextDependencies,
cacheable: resolveData.cacheable
};
callback(null, factoryResult);
}
}
}
复制代码
首先第一步是在 hooks.resolve.tapAsync
中:
- 调用
enhanced-resolve
Third party libraries get resources based on context 的绝对路径; - According to the resource suffix(文件类型)收集
webpack.config.js
中配置的 loader,得到最终的loaders
集合,这里涉及到 loader Order and the processing of inline and configuration way; - According to the above information is a creation module 时所需的数据 -->
createData
.
// webpack/lib/NormalModuleFactory.js
this.hooks.resolve.tapAsync({}, (data, callback) => {
// 创建一个 normal Resolve 实例
const normalResolver = this.getResolver("normal", resolveOptions);
let resourceData, loaders;
const continueCallback = () => {
... 一系列 loader 规则处理
// 生成 create module 相关数据集合
Object.assign(data.createData, {
layer:
layer === undefined ? contextInfo.issuerLayer || null : layer,
request: stringifyLoadersAndResource(
allLoaders,
resourceData.resource
),
userRequest,
rawRequest: request,
loaders: allLoaders,
resource: resourceData.resource, // Resources to the full absolute path
context:
resourceData.context || getContext(resourceData.resource),
matchResource: matchResourceData
? matchResourceData.resource
: undefined,
resourceResolveData: resourceData.data,
settings,
type,
parser: this.getParser(type, settings.parser), // module 的 parse Rely on collecting the parser
parserOptions: settings.parser,
generator: this.getGenerator(type, settings.generator), // module 的代码生成器
generatorOptions: settings.generator,
resolveOptions
});
callback();
}
// 执行 enhanced-resolve Third party libraries parse module path,得到 resolvedResource
this.resolveResource(
contextInfo,
context,
unresolvedResource,
normalResolver,
resolveContext,
(err, resolvedResource, resolvedResourceResolveData) => {
if (resolvedResource !== false) {
resourceData = {
resource: resolvedResource,
data: resolvedResourceResolveData,
...cacheParseResource(resolvedResource)
};
}
continueCallback();
}
);
})
复制代码
有了 module createData
,接下来就是创建 NormalModule
实例得到 module
:
this.hooks.factorize.tapAsync({}, (resolveData, callback) => {
this.hooks.resolve.callAsync(resolveData, (err, result) => {
const createData = resolveData.createData;
this.hooks.createModule.callAsync(createData, resolveData, (err, createdModule) => {
// 创建 module 实例
if (!createdModule) createdModule = new NormalModule(createData);
createdModule = this.hooks.module.call(createdModule, createData, resolveData);
return callback(null, createdModule);
})
}
})
复制代码
一个 module
实例上,记录了 parse 以及 loader 相关信息,Contains the common properties and methods:
// webpack/lib/NormalModule.js
class NormalModule extends Module {
constructor({ ...createData }) {
this.request = request;
this.parser = parser;
this.generator = generator;
this.resource = resource;
this.loaders = loaders;
this._source = null; // module 文件内容
}
createSource() {},
_doBuild(options, compilation, resolver, fs, hooks, callback) {}
build(options, compilation, resolver, fs, callback) {}
codeGeneration() {}
...
}
复制代码
create module 完成后,将 result 回传给 callback 即回到了 this.factorizeModule
的回调中执行 addModule
:
// webpack/lib/Compilation.js
handleModuleCreation({ ... }, callback) {
this.factorizeModule({ ... }, (err, factoryResult) => {
const newModule = factoryResult.module;
this.addModule(newModule, (err, module) => {
...
});
});
}
复制代码
4.4、addModule 存储模块
addModule AsyncQueue 的处理器是 _addModule
,将 module 添加到 modules
集合中:
// webpack/lib/Compilation.js
_addModule(module, callback) {
const identifier = module.identifier();
const alreadyAddedModule = this._modules.get(identifier);
if (alreadyAddedModule) {
return callback(null, alreadyAddedModule);
}
this._modulesCache.get(identifier, null, (err, cacheModule) => {
if (cacheModule) {
cacheModule.updateCacheModule(module);
module = cacheModule;
}
this._modules.set(identifier, module);
this.modules.add(module);
callback(null, module);
});
}
复制代码
module 添加完成后,执行 callback 回到 addModule
的回调中:
// webpack/lib/Compilation.js
handleModuleCreation({ ... }, callback) {
this.factorizeModule({ ... }, (err, factoryResult) => {
const newModule = factoryResult.module;
this.addModule(newModule, (err, module) => {
for (let i = 0; i < dependencies.length; i++) {
const dependency = dependencies[i]; // entry dep
moduleGraph.setResolvedModule(
connectOrigin ? originModule : null,
dependency,
module
);
}
this._handleModuleBuildAndDependencies(
originModule,
module,
recursive,
callback
);
});
});
}
复制代码
4.5、buildModule 构建模块
接下来执行 _handleModuleBuildAndDependencies
进入 build 阶段.
build Stage to do the following a few things:
- 创建 loader context 上下文;
- 调用
loader-runner
第三方库提供的runLoaders()
执行loader
进行代码转换,这里会传入resource
、loaders
、loaderContext
,After a conversion source code result; 3、执行createSource
创建RawSource
实例到module._source
上,通过_source.source()
To get the file converted the source code; - 执行
parse
(JavascriptParser) 对source
进行 ast 解析,Collection module depends on the collection tomodule.dependencies
中.
Let's take a look at the code on the implementation of the.
// webpack/lib/Compilation.js
_handleModuleBuildAndDependencies(originModule, module, recursive, callback) {
this.buildModule(module, err => {
...
})
}
复制代码
首先判断是否需要 build,Primary packaging will need to,接着执行 module.build
即 NormalModule.build
,并执行 _doBuild
进行打包.
// webpack/lib/Compilation.js
_buildModule(module, callback) {
module.needBuild({ ... }, (err, needBuild) => {
this.hooks.buildModule.call(module);
this.builtModules.add(module);
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
err => {
...
}
)
})
}
// webpack/lib/NormalModule.js
build(options, compilation, resolver, fs, callback) {
return this._doBuild(options, compilation, resolver, fs, hooks, err => {
...
})
}
复制代码
在 _doBuild
中创建 loader 执行上下文,通过 runLoaders
执行 loader 转换代码,最后得到 this._source
对象.
// webpack/lib/NormalModule.js
_doBuild(options, compilation, resolver, fs, hooks, callback) {
// 创建 loader 上下文
const loaderContext = this._createLoaderContext(resolver, options, compilation, fs, hooks);
// 生成 _source 对象
const processResult = (err, result) => {
const source = result[0];
const sourceMap = result.length >= 1 ? result[1] : null;
this._source = this.createSource(
options.context,
this.binary ? asBuffer(source) : asString(source),
sourceMap,
compilation.compiler.root
);
return callback();
}
// 调用 loader 进行代码转换
runLoaders({
resource: this.resource,
loaders: this.loaders, // 配置的 loader
context: loaderContext,
}, (err, result) => {
processResult(err, result.result);
})
}
复制代码
_doBuild 执行完毕后回到 build 作用域下,对经过 loader After the conversion source code parse ast
解析,Collect dependent modules,并生成 build module hash.
// webpack/lib/NormalModule.js
build(options, compilation, resolver, fs, callback) {
return this._doBuild(options, compilation, resolver, fs, hooks, err => {
const handleParseResult = result => {
this._initBuildHash(compilation);
return handleBuildDone();
}
const handleBuildDone = () => {
// 创建快照
compilation.fileSystemInfo.createSnapshot(..., (err, snapshot) => {
return callback();
})
}
const source = this._source.source();
// 这里的 parse 是由 JavascriptParser.js 提供
result = this.parser.parse(this._ast || source, {
source,
current: this,
module: this,
compilation: compilation,
options: options
});
handleParseResult(result);
})
}
复制代码
最后回到 build 时传递的 callback,将 module 存储在 _modulesCache
中:
// webpack/lib/Compilation.js
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
err => {
this._modulesCache.store(module.identifier(), null, module, err => {
this.hooks.succeedModule.call(module);
return callback();
});
}
)
复制代码
至此,module Phase is complete,回到 this.buildModule
中执行 processModuleDependencies
处理依赖模块.
// webpack/lib/Compilation.js
this.buildModule(module, err => {
this.processModuleDependencies(module, err => {
callback(null, module);
});
})
复制代码
4.6、processModuleDependencies
如果模块存在 dependencies
依赖,Pair will module call handleModuleCreation()
For the build steps,否则执行 callback Module to compile the end.
// webpack/lib/Compilation.js
_processModuleDependencies(module, callback) {
// Didn't have to deal with dependent on
if (sortedDependencies.length === 0 && inProgressTransitive === 1) {
return callback();
}
// Handle child depend on
for (const item of sortedDependencies) {
this.handleModuleCreation(item, err => {
...
});
}
}
复制代码
现在,All depend on the processing is complete,依次完成 this.handleModuleCreation
---> this.addModuleTree
---> compilation.addEntry
---> compiler.hooks.make
.
4.7、compilation.finish
进入了 compilation.finish
意味着模块的 make
Packaging production phase to complete.在这里,调用 hooks.finishModules
The process of building a and collecting module of errors
和 warnings
.
// webpack/lib/Compilation.js
class Compilation {
constructor(compiler, params) {
this.errors = [];
this.warnings = [];
}
finish(callback) {
this.factorizeQueue.clear();
const { modules } = this;
this.hooks.finishModules.callAsync(modules, err => {
for (const module of modules) {
// 收集 error
const errors = module.getErrors();
if (errors !== undefined) {
for (const error of errors) {
this.errors.push(error);
}
}
// 收集 warning
const warnings = module.getWarnings();
if (warnings !== undefined) {
for (const warning of warnings) {
this.warnings.push(warning);
}
}
}
this.moduleGraph.unfreeze();
callback();
});
}
}
复制代码
最后
感谢阅读.
第二篇介绍了「生成阶段」和「写入阶段」,Can walk to here to see webpack5 Packaging process source code analysis(2).
copyright notice
author[Mingli people],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/218/202208061858275438.html
The sidebar is recommended
- Deep understanding of volatile, Synchronized, and already the underlying implementation principle
- Spring common annotation analysis
- Docker quickly builds a PHP+Nginx+Mysql environment and steps on the pit diary
- Mac's own software - play with focus search
- What is the principle of the second type of dictionary method?
- Implementing a caching mechanism using soft references
- Vue entry page for the first time, how to distinguish between created and activated?
- Things to do with your new mac notebook
- [Java Interview] After 7 years of work, I went to the byte interview and turned around on this question. Could you please tell me your understanding of the time wheel?
- Optimization, genetic algorithm (ga)
guess what you like
Advanced Guide to Algorithm Competition Recursive Implementation of Permutation Enumeration
How to use the second type of dictionary method?
mac Maven error No compiler is provided in this environment. Perhaps you are running on a JRE rat
[Help] In the vue2 that the vue3 project is connected to as a subsystem, the whole project using el-drawer reports an error?
Redis basis
【Macbook software sharing|Use skills】Dry goods
Advanced Guide to Algorithm Competition Recursive Implementation of Exponential Enumeration
JAVA based - 11 - encapsulated inherited polymorphism - nine five small jean-marie le pen
Trend Micro Written Test Review (Algorithm Question 2)
Multilayer Perceptron - MLP
Random recommended
- (3) write their own - YOLOv3 - loss function
- Sketch91: How to set an aligned reference object and align it according to the specified element tutorial
- Target tracking actual combat deepsort+yolov5 (on)
- Tensorflow - recurrent neural network (5) subword text classification
- Linear Algebra and Probability Theory
- Big data "killed": who am I, where am I, and what have I (was) done?
- Study together to build the react + webpack + ts framework!!!!!!(a)
- Java Data Structures and Algorithms Lesson 9 - Sorting
- Hardcore!The internal manual "MySQL Notes" written by the technical director of Ali is really strong
- How Spring solves the circular dependency problem
- What is the goal of quantization coding?
- What are the quantification methods?
- I the first page of the scaffold - AST abstract syntax tree
- What is the core idea of the first type of dictionary coding method, can you give an example?
- iOSUIKit and Swift | Youth Training Notes
- Vue about embedded iframe page?
- Likou 93 - Restoring IP Address [Backtracking Algorithm]
- Why is run-length encoding a better encoding method for computer desktop images?
- What is the Cartesian product type in quantization coding?
- Cache series: cache consistency problem solution
- Vue family bucket - Vue-CLI2 and Vue-CLI3 hands-on teaching
- [Tips] Mac uses commands to view the size of sub-files or sub-directories in a directory
- Why build an index?
- Descartes set type and what is the effect of quantization coding?
- [Written in the romantic moment of Qixi Lang] The solution for obtaining data when encountering http codes 206 and 302 in Go
- [Operating System] Process Creation and Destruction
- AQS synchronization component - CountDownLatch analysis and case
- Why is there an index in quantization coding?
- What is the linear combination type in quantization coding?
- Arduino Painless Development _LED8*8 Dot Matrix Experiment (Detailed)
- element ui table changes the default style, removes the border and changes the table background color
- Data Structure ----- Quick Sort
- Node.js test SMTP service
- Create Nginx docker container reverse proxy https
- Python batch get gitlab project code
- Do phrases created by the second-class dictionary method have to have a specific meaning?
- How do I select the quantitative method for the quantitative characteristics of the coding?
- What is the result after straight-sum quantization?
- What are the types of high-dimensional indexes?
- Back-end writing Swagger interface management documentation