当前位置: 首页 > Web前端 > JavaScript

webpack流程分析(二):参数初始化完成

时间:2023-03-26 21:25:23 JavaScript

前言上面说过,webpack已经准备好参数了,是时候创建编译器对象了。创建完成后会执行compiler.run开始编译。本文将讲解从newCompiler到compiler.run()的过程。整个过程发生在createCompiler的函数体中。/***@param{WebpackOptions}rawOptions选项对象*@returns{Compiler}一个编译器*/constcreateCompiler=rawOptions=>{newCompilernew之前,webpack会完成一次基础参数的初始化,这里只输出日志格式和上下文被分配applyWebpackOptionsBaseDefaults(options);webpack/lib/Compiler.js是编译整个webpack的核心流程。在新建Compiler时,首先在Tapable中注册一堆hook,比如常见的watch-run、run、before-run等。可以在此处找到更多钩子。初始化文件操作newNodeEnvironmentPlugin({infrastructureLogging:options.infrastructureLogging}).apply(compiler);这里是扩展编译器对象,增加一些对文件的操作,比如输入、输出、监听、缓存等方法。同时也注册了一个beforeRunhook的回调。应用(编译器){const{infrastructureLogging}=this.options;compiler.infrastructureLogger=createConsoleLogger({level:infrastructureLogging.level||“info”,debug:infrastructureLogging.debug||false,console:infrastructureLogging.console||nodeConsole({colors:infrastructureLogging.colors,appendOnly:infrastructureLogging.appendOnly,stream:infrastructureLogging.stream})});compiler.inputFileSystem=newCachedInputFileSystem(fs,60000);constinputFileSystem=compiler.inputFileSystem;编译器.outputFileSystem=fs;compiler.intermediateFileSystem=fs;compiler.watchFileSystem=newNodeWatchFileSystem(compiler.inputFileSystem);compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin",compiler=>{如果(compiler.inputFileSystem===inputFileSystem){compiler.fsStartTime=Date.now();inputFileSystem.purge();}});}这里的fs不是nodejs的文件系统,而是使用第三方包graceful-fs注册插件if(Array.isArray(options.plugins)){for(constpluginofoptions.plugins){if(typeofplugin===“函数”){plugin.call(编译器,编译器);}else{插件.apply(编译器);}}}接下来,webpack会将在options中注册的插件进行注册,并传递给compiler对象,供插件内部使用。插件可以通过编译器提供的hook在整个编译过程中注册hook的回调函数。同时,一些编译器钩子传入编译对象,可以在资源构建时注册编译??钩子回调。如何写一个webpack插件环境就绪插件注册后,webpack重新给options赋默认参数。为什么会和之前的applyWebpackOptionsBaseDefaults.这里调用了applyWebpackOptionsDefaults(options);又增加了一波默认值。添加后调用环境和afterEnvironment钩子。compiler.hooks.environment.call();compiler.hooks.afterEnvironment.call();注册内置插件环境初始化后,webpack还需要执行自己的内部默认插件。新的WebpackOptionsApply().process(选项,编译器);这里会根据你的配置执行相应的插件。挑几个和hooks相关的,分析entrynewEntryOptionPlugin().apply(compiler);compiler.hooks.entryOption.call(options.context,options.entry);这里是生成构建需要的入口数据结构/**@type{EntryOptions}*/constoptions={name,filename:desc.filename,runtime:desc.runtime,layer:desc.layer,dependOn:desc.dependOn,publicPath:desc.publicPath,chunkLoading:desc.chunkLoading,wasmLoading:desc.wasmLoading,library:desc.library};然后调用EntryPlugin注册Compiler.hooks:compilation,在appplay方法中做这两个钩子函数。以后在compiler对象中触发makehook时,EntryPlugin中注册的回调会触发complition.addEntry(context,dep,options)开始编译。这是关键点,否则初始入口compiler.hooks.make.tapAsync("EntryPlugin",(compilation,callback)=>{compilation.addEntry(context,dep,options,err=>{callback(err);});});webpack自身注册resloverFactoryhook后,一些插件调用完成,会调用afterPluginhook。compiler.hooks.afterPlugins.call(compiler);接下来webpack在compiler.resolverFactory上注册了resolveOptions钩子compiler.resolverFactory.hooks.resolveOptions.for("normal").tap("WebpackOptionsApply",resolveOptions=>{resolveOptions=cleverMerge(options.resolve,resolveOptions);resolveOptions.fileSystem=compiler.inputFileSystem;returnresolveOptions;});compiler.resolverFactory.hooks.resolveOptions.for("context").tap("WebpackOptionsApply",resolveOptions=>{resolveOptions=复制代码cleverMerge(options.resolve,resolveOptions);resolveOptions.fileSystem=compiler.inputFileSystem;resolveOptions.resolveToContext=true;returnresolveOptions;});compiler.resolverFactory.hooks.resolveOptions.for("loader").tap("WebpackOptionsApply",resolveOptions=>{resolveOptions=cleverMerge(options.resolveLoader,resolveOptions);resolveOptions.fileSystem=compiler.inputFileSystem;返回解析选项;});这里的目的是为Factory.createResolver提供一个默认的参数对象(包含相关的resolve项目配置项),然后调用afterResolvershookcompiler.hooks.afterResolvers.call(compiler);初始化完成至此,compilerobject上的东西已经够多了,可以开始我们的编译了,也就告诉外界初始化完成了。以webpack的调性,必然有钩子触发。编译器.hooks.initialize.call();结束语webpack编译之前的一切都已经解释清楚了,下一篇开始编译。请读者记下文中提到的各种hook的注册。在整个编译过程中,一些较早注册的钩子往往会在你错过的地方被触发。这也是调试webpack过程中的一个痛点。