webpack解析初探

最近花了一点时间探究webpack5的源码,通过网上的一些文章的辅助去更好的理解这个项目。接下来分析一个常规的解析过程。

首先是是使用vscode的Node调试,通过vscode的run and debug添加调试配置,默认生成的launch.json文件如下{

    "version": "0.2.0",

    "configurations": [

        {

            "type": "pwa-node",

            "request": "launch",

            "name": "Launch Program",

            "skipFiles": [

                "<node_internals>/**"

            ],

            "program": "${workspaceFolder}\\mytest\\index.js"

        }

    ]

}

配置program调试入口,然后点击三角形按钮就可以调试代码。

 

 

如图在新建了一个mytest的文件夹下创建一个index.js作为调试的入口,掉用lib/index.js。引入webpack在lib/webpack.js中导出函数,调用这个函数一步步往下执行。

在开始之前我们先来了解一下Hooks类使用的是tapable, 它包含几个方法SyncHook,

    SyncBailHook, 

    AsyncParallelHook,

    AsyncSeriesHook

通过tap注册事件和回调,并通过call,callAsync,promise.then来触发。当然还可以注册拦截器来监听hook执行的各个阶段。

Tapable是webpack的精髓,通过hook注册和触发事件,贯穿整个构件解析的生命周期,后面的解析我们会讲到,compiler以及compiler.compilation都注册了很多操作的Hooks.

接下来我们正是进入源码,lib/webpack.js中导出的函数,主要是create函数创建并返回compiler实例和watch标记以及watchOptions配置。

当我们的调用的函数存在回调是会判断是否是watch,是的话执行compiler.watch,否则执行compiler.run。

const { compiler, watch, watchOptions } = create();

                if (watch) {

                    compiler.watch(watchOptions, callback);

                } else {

                    compiler.run((err, stats) => {

                        compiler.close(err2 => {

                            callback(err || err2, stats);

                        });

                    });

                }

                return compiler;

可以构件单个compiler调用的是createCompiler,后者多个createMultiCompiler,下面我们一单个为例

const createCompiler = rawOptions => {

    const options = getNormalizedWebpackOptions(rawOptions);

    applyWebpackOptionsBaseDefaults(options);

    const compiler = new Compiler(options.context, options);

    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);

            }

        }

    }

    applyWebpackOptionsDefaults(options);

    compiler.hooks.environment.call();

    compiler.hooks.afterEnvironment.call();

    new WebpackOptionsApply().process(options, compiler);

    compiler.hooks.initialize.call();

    return compiler;

};

首先是获取用户的配置加上默认的配置,生成最新的配置。然后创建compiler实例,

NodeEnvironmentPlugin插件是用来扩展node,加文件缓存、文件读取和写入、文件监听。这里文件监听使用的是Watchpack库。

执行用户配置的插件,在添加webpac默认配置。

接下来所有重要的逻辑都是在WebpackOptionsApply这里面处理的,通过配置去执行对应的插件。

compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {

            compilation.addEntry(context, dep, options, err => {

                callback(err);

            });

        });

主要来看入口文件的处理, EntryOptionPlugin插件中EntryPlugin通过调用compilation.addEntry从入口文件开始处理。

此外如果入口是一个函数,则调用DynamicEntryPlugin也会添加入口文件。

在网上看到别人分析的源码,我觉得这张图结构很清晰,可以说明的生命周期的各个阶段所做的事:

 

addEntry方法中调用addModuleTree去处理相关的模块依赖,调用dependencyFactories

处理模块的依赖dependency.constructor。然后handleModuleCreation内部处理,最后调用factorizeModule解析模块。用factorizeQueue将模块解析压入模块解析队列中执行。

首先调用工程的factory.create函数,调用的是compilation.dependencyFactories.set(

                    EntryDependency,

                    normalModuleFactory

                );

放入的normalModuleFactory。

大致步骤addModule添加模块,然后_handleModuleBuildAndDependencies构建模块buildModule和处理模块依赖processModuleDependencies。通过_processModuleDependencies方法中handleModuleCreation循环构建。

在构建过程中需要处理一些loader,在NormalModule中调用_doBuild中的runLoaders去处理,最终返回资源和webpackAST信息。

最后调用compilation.seal将模块转换成chunk代码块,最后通过emitAssets输出合并后的代码块输出文件。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值