前言上面我们拿到了构建module的factory、dependencies等关键数据,通过addModuleTree将factorizeQueue的控制权交给了factory.create。这时reslove过程开始。本文主要分析NormalModuleFactory内部的beforeResolve、factorize、resolve、afterResolvehooks。配置文件本文周边的配置文件如下:module.exports={mode:'production',entry:'./src/index.js',output:{filename:'main.js',path:路径。resolve(__dirname,'dist'),},模块:{规则:[{test:/\.(js?|tsx?|ts?)$/,使用:[{loader:'babel-loader',},],},]},resolve:{extensions:['.js','.ts'],别名:{demo:path.resolve(__dirname,'src/second'),},},};factory.create条目从Factory.create开始。这里的工厂就是之前addModuleTree获取到的NormalModuleFactory。NormalModuleFactory首先触发其内部的beforeResolve钩子,然后在回调中执行factorize钩子函数。factorize挂钩调用再次解析。这里好像比较绕,简单说明一下:hooks的调用顺序是这样的。beforeResolve->factorize->resolvebeforeResolve没有找到之前注册的地方,好像什么都没做。也可能是我之前没有在ExternalModuleFactoryPlugin插件中找到factorize并注册,这里会处理外部信息。resolvehook注册在NormalModuleFactory内部,用于解析模块并生成相应的loader和依赖信息。这里重点是resolvesolvegetLoaderResolver进来的第一步,resolve钩子首先调用this.getResolver("loader")返回loaderResolver,可以理解为解析loader的方法。简单来说分为以下几个步骤:调用ResolverFactory中的get方法判断是否有对应类型的缓存创建resolveOptions,调用require("enhanced-resolve").ResolverFactory创建resolver,然后返回NormalModuleFactory继续执行代码。constloaderResolver=this.getResolver("loader");loaderResolver公开了一个解析器方法来解析加载器。normalResolver接着往下走,跳过一些判断,直接进入defaultResolve方法,这里会根据webpack配置文件中的resolve选项生成一个normalResolver。同样,这个normalResolver也是require("enhanced-resolve").ResolverFactory的一个实例,同样暴露了一个resolve方法。constnormalResolver=this.getResolver("normal",dependencyType?cachedSetProperty(resolveOptions||EMPTY_RESOLVE_OPTIONS,"dependencyType",dependencyType):resolveOptions);接下来,这个normalResolver和一些上下文信息会被传递给resolveResource方法,最终会调用node_modules/enhanced-resolve/lib/Resolver.js的doResolve。this.resolveResource(contextInfo,context,unresolvedResource,normalResolver,resolveContext,(err,resolvedResource,resolvedResourceResolveData)=>{if(err)returncontinueCallback(err);if(resolvedResource!==false){resourceData={资源:resolvedResource,数据:resolvedResourceResolveData,...cacheParseResource(resolvedResource)};}continueCallback();});然后根据doResolve返回的resolvedResource和resolvedResourceResolveData组装resourceData。稍后我们将在解析加载程序时使用它。resourceData数据结构分析加载器在resolvedResource的回调中继续执行constresult=this.ruleSet.exec({resource:resourceDataForRules.path,realResource:resourceData.path,resourceQuery:resourceDataForRules.query,resourceFragment:resourceDataForRules.fragment,scheme,断言,mimetype:matchResourceData?“”:resourceData.data.mimetype||“”,依赖:dependencyType,descriptionData:matchResourceData?undefined:resourceData.data.descriptionFileData,发行者:contextInfo.issuer,编译器:contextInfo.compiler,issuerLayer:contextInfo.issuerLayer||""});这里会根据配置文件中的规则获取需要的loader。在这个例子中,我们的result会遍历这个result,生成useLoadersPost、useLoaders、useLoadersPre。然后调用resolveRequestArray获取postLoaders、normalLoaders、preLoaders。this.resolveRequestArray(contextInfo,this.context,useLoaders,loaderResolver,resolveContext,(err,result)=>{normalLoaders=result;continueCallback(err);});当前示例没有postLoaders和preLoaders,这里只有normalLoaders。resolveRequestArray内部调用loaderResolver.resolve解析useLoaders,最终结果是将result中的loader替换为对应的真实文件地址。{ident:undefinedloader:'/Users/csy/Code/webpack5/node_modules/babel-loader/lib/index.js'options:undefined}生成回调数据最后,continueCallback处理下已经生成的数据首先用于合并的装载机。合并postLoaders、normalLoaders、preLoaders。然后赋值data.createData,这个数据来自钩子入口传入的数据。Object.assign(data.createData,{layer:layer===undefined?contextInfo.issuerLayer||null:layer,request:stringifyLoadersAndResource(allLoaders,resourceData.resource),userRequest,rawRequest:request,loaders:allLoaders,资源:resourceData.resource,上下文:resourceData.context||getContext(resourceData.resource),matchResource:matchResourceData?matchResourceData.resource:未定义,resourceResolveData:resourceData.data,设置,类型,解析器:this.getParser(类型,settings.parser),parserOptions:settings.parser,generator:this.getGenerator(type,settings.generator),generatorOptions:settings.generator,resolveOptions});这里重点关注getParser和getGenerator,这两个方法返回解析器和构建模板的方法。根据当前示例,返回的是一个JavascriptParser和一个JavascriptGenerator。然后在createModule中会用到这个createData。在执行NormalModuleFactory的afterResolve钩子后constcreateData=resolveData.createData;this.hooks.createModule.callAsync(//something)reslove结束了,接下来就要开始了,创建模块!总结模块解析过程用于获取各个加载器和模块的绝对路径等信息。在resolverhook中,首先通过enhanced-resolve获取loaderResolver,并提供resolve方法。在defaultResolve方法中,获取normalResolver,并提供resolve方法。解析unresolvedResource,获取文件的绝对路径等信息,根据规则获取loader,使用loaderResolver获取loader的绝对路径等信息,合并loader,拼接数据,调用NormalModuleFactory的afterResolvehook,结束解析过程。
