




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
全文3000字,欢迎点赞转发事情是这样的,某天有个小伙伴问我:为啥我的webpack运行完看不到我写的页面,而是:listingdirectory/。。①localhost9000distOwebpack.conf^g.js嗯?文件列表页?好吧,这种情况我似乎没遇到过,一下子没法给出答案,只能要来关键代码:isisindex.isux tD0CD…test-hmr>src>nindex.js>一1constnod*■document.createEle«ent('(Jlv*)2node.innerText,'test,3 document.body.appendCh1Id^node^test-hmr>public><>index.htmi>伞htrnl>0body<100CTYPfhtal>a<hc*d>eetacharset•MUTF-8-><aetdhttp»«quiv«"X-UAYy>atible"content,”IE**6 <metanames"viewport*content^'width^device-«ld<tltie>Docu«ient*/title*</head><t>ody>1•</body>
•webp«ck.c8glsUxtest-hmr>.webpack.config.js>M〈unknown〉>fldevServ1 constpath•require("pathM);constHtalWeb^ackPlu9m=require(,htal-webpackaodule.exports"{entry:*./src/indexMfaode:"developsent。devtool:false,output:{filename:M(naae).jsNtpublicPath:path:path.Join,_dlrna«ef》•devServer:《coopress:true,inline:true,hot:true.port:9«M,open:true19乐plugins:InewHtalWebpackPlugin({tcaplate:*./public/index.htaVTOC\o"1-5"\h\z})】}:重点看看wehpack.config.js配置,用到devServer+HMR功能,其中:Webpack版本为5.37.0webpack-dev-server版本为3.11.2看了半天,没问题呀,给了几个纸糊的建议还是解决不了问题,刚好在开会这事就暂且放下了。过了一会,小伙伴兴冲冲跑过来跟我说经过一番盲猜,问题被解决了:output.publicPatk=时一'切正常output.pu匕〃ePath=7,时出错,返回文件列表页啊?这玩意还会影响aevSewer的效果,直觉告诉我不应该啊。emmm,成功勾起我的好奇心了,虽然写过一些Webpack源码分析的文章,但webpack-dev-server确实不在我的知识范围,好在我有秘籍《如何阅读源码——以Vetur为例》,是时候展示真正的技术了!第一步:定义问题先复盘一下问题发生的过程:webpack.co八fig.js同时配置了oupat.pub/icP戊人与devServer运行npxwebpackserve启动开发服务器浏览器访问http:〃(ocalhost:q。。。没有按预期返回用户代码,而是返回了文件列表页面;但如果恢复。〃中戊.小加icP〃协的默认配置,一切如常讲道理,。叩戊.仆如百〃儿应该只是影响了最终产物引用的路径,试试命令行工具运行检测首页返回的内容:,~curlhttp://localhost:9000<?DOCTYPEhtml><head><metachorset・'utf-8'>rnntFnt."*idtb»device・width,initial*scale-1.0tmaxxmum-scale-1.0,user-sealobie-n<title>listingdirectory/</title> margin:0;padding:0;outline:0;)body{podding:80Px100px;font:13px"HelveticaNeue","lucid。Grande","Arial";background:#ECE9E9-webkit-gradient(linear,0%0%,0%100%, to(#ECE9E9));background:#ECE9E9-moz-linear-gradient(top,#fff,#ECE9E9);background*repeat:no-repeat;color:#555;-webkit-font-smoothing:antialiased;)hl,h2,h3{font-size:22px;color:#343434;}hl5,h2em{podding:0Spx;Tips:有时候可以试试绕过浏览器的复杂逻辑,用最简单的工具验证http请求返回的内容。可以看至“,请求kttp:〃/oca/host:qooo地址返回一大串html代码,且页面的title为listingdirectory 也就是我们看到的文件列表页面:listingdirectory/<-->CO①localhost9000(Search~/国dist ⑦public Nsrc@webpack.conf^g.js虽然不知道这是在那一层生成的,但可以肯定绝对不是我写的,而且这是在HTTP层面发生的。所以问题的核心就是:为何Webpack的output.pub/icPath会影响webpack-dev-ierver的运行效果?第二步:回顾背景带着问题我又review了一遍Webpack官方文档。publicPath配置首先output.publicPath是这么描述的:Thisisanimportantoptionwhenusingon-demand-loadingorloadingexternalresourceslikeimages,files,etc.Ifanincorrectvalueisspecifiedyou'llreceive404errorswhileloadingtheseresources.大意就是,这是一个控制按需加载或资源文件加载的选项,如果对应的路径资源加载失败时会返回404„瞎,其实这段描述就非常不明所以了,简单理解。协会改变产物资源在html文件的路径,比如说Webpack编译完生成「bum.dle.js文件,默认情况下写到html的路径是:(scriptsrc="bundle.js"/>如果设置了output.publicPath值,就会在路径前增加前缀:〈scriptsrc="${output.publicPath}/bundle.js"/>看起来很简单。devServer配置项再来看看de.vSe.rver配置:Thissetofoptionsispickedupbywebpack-dev-serverandcanbeusedtochangeitsbehaviorinvariousways.大意就是,devServer配置最终会被webpack-dev-server消费,而webpack-dev-server提供了包括HMR 模块热更新在内的web服务。感受一下,包括vue-cli、create-react-叩p之类的脚手架工具底层都依赖于webpack-dev-server,它的作用和重要性就可想而知了吧。第三步:分析问题按照现有的情报,加上我对HTTP协议的理解,可以基本推断问题必然是出在webpack-dev-server框架处理首页请求的逻辑上,大概率是output.publicPatk属性影响到首页资源的判定逻辑,导致webpack-dev-server找不到对应的资源文件,返回兜底的文件列表页面。嗯,我觉得靠谱,那就沿着这个思路挖一挖源码,找到具体原因吧。第四步:分析代码结构分析书上得来终须浅,debug还需看源码啊,啥都别说了先打开webpack-dev-server包的代码看看内容吧:EXPLORER•・・ (}package.jsonXLEARN-WEBPACKnode_modules>webpack-dev-server>{}package.json>I7webpack-dev-server1{2“name":Hwebpack-dev-serverH,>bin3••version**:>client4,•description**:HServesawebpackapp.Updaylib5"bin":Hbin/webpack-dev-server.jsH.yservers67"main":Mlib/Server.jsHf•,files**:]•■bin”,JSBaseServer.js8JSSockJSServer.js9MUbHfJSWebsocketServer.js10MssVt|>utils11“client”()options.json12131]»••engines**:{JSServer.js14“node":M>=6.11.5M>node_modules>ssl15),>Debug0CHANGELOG.mdHLICENSE161718,•scripts**:{Mlint:prettier1*:HprettierHeslint.—cache**,{}package.json19Hnpm-run-all-I-p(DREADME.md20"lint:type":Htsc—noEmitMr>webpack-log21Mcommittint11:"commitlint—from=masterHTips:读者也可以试试clonewebpack-dev-server仓库的代码,有惊喜\~\~项目结构并不复杂,按Webpack的习惯可以推断主要代码都在他目录:・webpack-dev-servergit:(master)xcloc./lib27textfiles.27uniquefiles.0filesignored.github.co(n/AlDaniaUclocv1.90T=0.075;(376.2files/s.39269.3lines/s)LanguagefilesblankccxwnentcodeJavaScript263872801652JSON100499SUM:273872802151c/oc是一个非常好用的代码统计工具,官网:/package...代码量也就2000出头,还好还好。接下来再打开packagejson文件,看看有哪些depenWcncg,一个个捋过去之后,与我们的问题强相关的依赖有:express:应用不用多介绍「吧webpack-dev-Middleware:这个应该大多数人没有注意过,从官网文档判断这是一个桥接Webpack编译过程与express的中间件serve-/Wex:提供特定目录下文件列表页面的express中间件!!!按照这个描述,这锅肯定出在serve-iMcx的调用上啊,感觉离答案很近了。局部分析
切入点:验证serve-讥4ex包的作用经过上面的分析,虽然我还不知道问题具体出在哪里,但大致可以判定跟serve-认Hex包强相关,先搜一下webpack-dev-server在哪些地方引用这个包:SEARCH Q==[*g)jsServer.jsX■•rvindw •正♦ lib>isServerjs>七Server>@setupStiticS«fveindexFeature>®options.static.forE»MmIoMuda .,= ..t93。 setupStaticScrvelndexFeature(>{。的 931 serveindex=require(serve-IMcX*);MmtoexcludeG 933■■i 9342resultsin1file-Openineditor 9357AServer.%;ib 2constserveindex=r»quire("serve-index*);IIserve-indexdoesn'tfaNthroughnon-get… 93994S942943945946947948949950951952this.options•stat1c•f0rf»ch((staticOption)«>{staticOption.publicPith.for€ach((pubVlcPath)»(if(staticOption.serveindex){this.app.use(publlcPatht(req«restnext)=>{〃msrvc-*indexdoesn't[through t/hesdrequestif(req.Mthod!«*GET*Hreq.aethod!―“HEAD")国returnnext();serveindex(staticOption.directory,staticOption.servelndireq,res,next);»;>»:»;)很幸运,只在他/Server”文件中用到,那就简单多了,静态分析调用语句前后的语句,大致上可以推导出:serve伉Hex调用被包袅在汝is.app.use内,推测this.app指向express不例,use函数用于注册中间件,所以整个serveindex就是一个中间件除setupStaticServelAd.exFeature外,Server类型中还包含了其它命名为setupXXXFeature的函数,基本上都用于添加express中间件,这些中间件组合拼装出webpack-dev-server提供的HMR、proxy、ssl等功能
也看不出别的啥了,先做个对照实验,运行起来动态分析代码的实际执行过程,验证到底是不是这个地方出错吧。先在serveIndex函数之前插入原bugger语句,之后:•先按照正常情况,也就是output.pub/icPatk=7,执行ndbnpxwebpackserve,结果是如常打开了页面,没有命中断点,没有中断•再按照ouput.pu匕〃ePath=执行ndbnpxwebpackserve,进入断点:ICcmo*SoiMMKvtanwvBM«maryFUwabvatfi .Swvql».G? [6ICcmo*SoiMMKvtanwvBM«maryFUwabvatfi .Swvql».G? [6 \*0conttceRtMtteM•t- ・・*■■■・・4*74M…7a•薪;“芸毛-ie<A"”-sArtey(Cf < ♦-1<>411CORtMtlMe.f(lt«a)9( Mthl""thl«.a0t.vw(contt*»tB»M*«ibUcFattprotocoli,-p・if 一■r,l——»9NfyiO4|J力(E.arthod WU ・rMlWri(224144U.r^ratun:II•F*“ L>・」MrwInd«a(it«Bt<Icsmttrw)•26 Mh"F41tMcurvi(•••141*>): •Mckeftte<Mt<<4«MectU*9tQ*3.J429>**** < «t«Ut(...)11*Ab«elut«UcUStrMf(ce«tcat4?4e.,neat)。•rw•rafl naaa«Mr*>■H(rvq.Mthod,-GTTMythod■MAO")<etc,。iwatdl««•>,•A«rf(•rr)*vq> <ndaijlTI4Jt1 __•r0WSf*W c<. zz'-“,7(anwMMlkx»<%t14M413Mrvvlntfex(cef*t«NtlM«(<Uvmtrue>)(rtq.r»(n«Hl)4344)$>caflbackWvnpotna4J4MtufMBtchStaticF«atur«()(4]74XMUUMolselKKStrUtfluettew))||typ«of**nooBMinw*1MVW.QR,owf>Crr»r(rtaot<ftie*Itm»tsupported*4144?)«1mif(Array.l»Arr»y(ceart«>itBaM)l<C0Rfefttt«M.f»rtACh((ite«)o<44J,,4••*>-©UtWrU$trUf(!««•))||typeof1..Ml r7<>Un»<n.C«Mif -nmnasS•wttopecMconho-i*/tiet1port:MM,open:',writeToOlsh:dlcFath:n««Nt«llM^eckFlavMi((taapUt«t'./pMBUcyiMea.r>•wet*ecioonAgjt、M >了 ,jaicoR«t*«(h•r«4»irv(*Mtw*)|■o^ul*.•apartt•(•utryta*./trc/lM»a'*laod«:fl:output:4Tips:ndbTips:ndb是一个开箱即用的nodedebugger工具,不需要做任何配置就能调试node应用,非常方便0K,答案揭晓了,在ouput.pub/icPatk=,/场景卜会命中这个中间件,执行serve伍Hex函数返回文件目录列表,这很makesense。不过,作为一个有追求的程序员怎么会止步于此呢,我们继续往下挖呀:到底是那一段代码决定了流程会不会进入serve伉dex中间件?切入点:确定serveindex的上游中间件思考一下,express架构的特点就是间通过next思考一下,express架构的特点就是间通过next函数调起下一个中间件。基于中间件的洋葱模型,而中间件之ResponseRequest嗯,有思路了,我们沿着webpack-dev-server的middleware队列,找到serve/ndex之前都有哪些中间件,分析这些中间件的代码应该就能解答:到底是那一段代码决定了流程会不会进入serve伉dex中间件?但是,express中间件架构下,从调用到实际中间件函数隔着很远的调用链路,很难通过断点的调用堆栈判断出上一级中间件,以及更更上一级中间件在哪里啊:
这时候不能硬刚,得换一个技巧了——找到创建express示例的代码,用魔法包裹住use函数:ndbConsoleSourcesPerformanceMemory㈤webpack.conftg.jsnoderfsServer.jsxindexes»J,,167 }168}).apply(piler);169 }170171701711721731741751761771781791801811821831841851861871881891901911Q2//Initexpressserver//eslint-disable-next-linenew-capthis.app=newexpressO;const_use=this.app.use;.app.yse=(・・・arg)=a{ara«[f]debug^ '_use7catl(this.app,...arg);目标就是塞进debugger命令setupHooksO{//ListeningforeventsconstinvalidPlugin=()=>{this.sockWrite(this.sockets,•invalid1);};constaddHooks=(compiler)q{const{♦app.u-SwvecjsetupCompressFeatureSorvorJcompressServerj♦app.u-SwvecjsetupCompressFeatureSorvorJcompressServerj(anonymous)Server.)setupFeaturesServer.jServerServer.)startDevServer startDevServer(anonymous)Index.jprocessTicksAndRejectionsI匕G+T,»•无。iDebuggerpaused►Nodeprocesses►Watch▼CallStacknodeilntemal/p...task.queuawaitinprocessTicksAndRejectionB(listener index.)_parseCommand{}Line178,Column7Tips:这种技巧在某些复杂场景下特别有用,比如我在学习Webpack源码的时候,就经常配合Proxg类对hook植入debugger语句,追踪钩子被谁监听,在哪里被触发通过这种重写函数,植入断点的方式,我们就能轻松追溯到webpack-dev-server用到了哪些中间件,以及中间件注册的顺序:
setupCoi^pre^Feature=>注册资源压缩中间件setupMidd/ew〃忆=>注册vjebpack-dev-middleware中间件set〃pStaticFe〃ture=>注册静态资源服务中间件setupServe伉dexFe〃t=>注册serve伍dex中间件可以看到,在当前Webpack配置下总共注册了这四个中间件函数,按照express的执行逻辑这四个中间件会按注册顺序从上往下执行,所以serve伉dex函数的直接上游就是setupStaticFeature注册的静态资源服务中间件了。继续看看setupStaticFcature函数的代码:362363364365362363364365366>379>396>414415416417418419420421constcontentBase=this.options.contentBase;constcontentBasePublicPath=this.options.contentBasePublicPatbif(Array.isArray(contentBase)){}elseif(isAbsoluteUrl(String(contentBase))){-}elseif(typeofcontentBase='number'){-?else国//routecontentrequestthis.app.use(contentBasePublicPath,express.static(contentBase,this.options.staticOptions));s}这里只是调用标准化的[expresss仅比J5ttp函数,注入静态资源服务功能,如果这个中间件运行的时候按路径找不到对应的文件资源,会调用下一个中间件继续处理请求,看起来跟我们的问题没啥关系。继续往上,看看setupMiddleware函数:
220221222220221222223224225226227228229230231232233234this•app.use(this•niddleware);setupDevMiddleware(){//middlewareforservingwebpackbundlethis.middleware=webpackDevMiddleware(piler,Object.assign(<}fthis.options,{logLevel:this.log.options.level);)注册了\^ebpack-dev-i^iddle\A/are,从名字就可以看出这个中间件跟weSpack-dw-server应该关系匪浅,那就继续打开\A/ebpack-dev-w\iddle\A/are看看里面的代码:TOC\o"1-5"\h\z〜webpack-dev-middleware 38I... 39~lib40JScontext.js 49JSDevMiddlewareError.js 59JS fs.js 60JS middleware.js 61• 63JSreporter.js64JSutil.js 65>node_modules 660CHANGELOG.md 67108Klicense 108Klicense 109{}package.json@README.md我去。。。也不少啊,这看起来太费劲了,我只是想找到这个bug的原因,没必要全看吧!那就直接搜关键词publicpath试试吧:7webpack-dev-middleware'libJScontext.jsJSDevMiddlewareError.jsJSfs.jsJSmiddleware.jsJSreporter.jsJSutil.js>node_modules©CHANGELOG.mdJSindex.jsHLICENSE{}package.json©README.md38394049383940495960616364656667107108109webpack-dev-middleware,/lib/middleware.js文件中被使用了1次\A/cbpack-dev-i^iddle\A/are/lib/util.js文件中被使用了23次那,就先挑软柿子捏,看看出以山ew〃忆Js文件中是怎么用的:const{getFileiaakv\eFroi^Url}=require(,./util,);^odule.exports=Functionw/apperfc。八七ext){returnfunctionMiddlewafe(ireq,next){functiongoNext。{//...resod(八ext());)//...letfileMkv\e=getFilcnakv\eFrokv\Url(context.options.publicPath}context.coi^piler,req.url);if(filename---false){returngoNextQ;}returnnewProFw/se((reso(ve)=>{ha^dleR.equest(contextJf〃e八〃Me,processReq〃est,req);//...1););注意代码中有一*个逻辑,就是调用util文件的八〃hAeF/OFvvCM函数,并判断返回的fileMkvxe值是否为Fake,是的话调用next函数,这看起来很像那么回事/!那就继续进去看看getFileiaai^eFroi^Url的代码:JSmiddleware'sJ$util.js xnode.modules>webpack-dev-middleware>lib>JSutil.js>[e]<unknown>>0getFilenameFromUrl60module.exports={61getFilenameFromUrKpubPath,compiler,url){62 const{outputpath,publicPath}=getPaths(pubPathfcompiler,url);63 //localPrefixisthefolderourbundleshouldbein64constlocalPrefix=publicPath11*/\false,true);65 consturlObject=^dFse(url);66 letfilename;67consthostNameIsTheSame=localPrefix.hostname=urlObject.hostname; //publicPathhasthehostnamethatisnotthesameasrequesturl*s,should1if1TOC\o"1-5"\h\zlocalPrefix.hostname !- null &&urlObject.hostname != null 3!hostNamelsTheSameg{76 returnfalse;77 }7879 //publicPath isnotinurl,soitshcLidrail80if(publicPath&&hostNamelsTheSame图url.indexOf(publicPath)!=6){81 returnfalse; ,82 )8384 //striplocalPrefixfromthestartofurl85 if(urlObJect.pathname.indexOftlocalPrefix.pathname)=0){86 filename=ur10bject•pathname.substr(localPrefix.pathname.length);87 }oo逐行分析下来,注意看红框框出来这一句:rffxxx&&url.i^dexOf(publicPath)!==O){returnFake;
讲道理,从字面意义上这个〃”应该是客户端发过来的请求连接,仆McP%应该QlocatKMtJOOO•Mwbpacfc.conAg.就是我们在webpack.co八figjs中配置的output.publicPath项的值了吧?运行起来看看:QlocatKMtJOOO•Mwbpacfc.conAg.>>webp«c*ieof*9i«>M<urMino«ft»>»pkjgintI 3tMpet1**l"W'i2 constKt«lMebo«ck*lu9in■rc^uiretISM6gsMMssMs7»nnn”nMnmr>MllguMM*oIwabcaatAaonto.San*.S.»■<9«tFileftaaeFroaUfUout*F«th4coapiUr,«rt)<ISM6gsMMssMs7»nnn”nMnmr>MllguMM*oIwabcaatAaonto.San*.S.»■<9«tFileftaaeFroaUfUout*F«th4coapiUr,«rt)<■.,(out(uf^ath,pvbllc^ath)•9•t^ethstp//l»c«lFre^xa1cthefolilrrpyrOuntfte>Mw[]c««t •p»rt«<»ublU*Bth||V,c«t«ti»rl06)«ct•parM<url)|urlObject•VrWtf■uMeftAMCM*ttestMMalsTMSMt-UcalFrvfU.hMtA〃 IkMtMA0tr*M*tA*f11(WffhItoe41mx1■■suit44urUWJtct.hottMMe!••aUUsQjecQ■'hottMMcBTheSMeMftNaMltTMSaK•tn•I<.r«twrn>PIf ftlnsssnrsHTisre2cfalMj>〃”r“l-t/ef"fra*tM”xr。,ufllOhtMft.nathnM^.ifwWaOf<larAl^t»ft«.gta.CoMm90 QOdMXiat•CMbugvarpauaad•CaBSiac*•Scop0,L«C«l»c(wH«nC-It”,<Moktt3.naaeidwfim.parent.ni«*awiu»d«nhottMaaeXsTh«MM:trve•loc«l>r«fiatJr'.<>r»<oc«lt”,;'、. ”.,:.wtht_vel:*/•'•"・91■"1<pret*coli"■---, : -dueGlttel qUtel0FM«ipaM»Egentry:•"■dMek.■o4e;"tfewlovaent**dsvtool:f«lw,OUtftft:<fit——t*11L-[*/h<U<M9rV<,.|'—iniNY,.gkiH_JHW.”>.devScrver:(c«ap,cs:trw«Mtitrw4part:M
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中小学教师资格笔试备考中的核心价值与理念实践试题及答案
- 信息系统项目管理师考试教材精讲试题及答案
- 2024年信息系统项目管理师考生准备试题及答案
- 2025年育婴师考试中团队合作的必要性及试题及答案
- 临床执业医师考试外科学试题及答案
- 临床执业医师考试的疾病处理试题及答案
- 2024西医临床重要知识点试题及答案
- 2024年药剂考试复习策略试题及答案
- 2024年信息系统项目管理师备考中的常见挑战试题及答案
- 人才引进中的程序与标准试题及答案
- 安全生产教育培训制度管理办法
- 抽油井检泵作业课件
- 2022年06月2022年广东肇庆广宁县司法局招考聘用政府雇员名师点拨卷V答案详解版(3套版)
- 《HSK标准教程3》第5课课件
- HSK标准教程4上第1课课件
- 民俗学概论 第一章 概述课件
- 养老机构行政值班查房记录表格
- 干粉灭火器点检记录表(样表)
- 伍光和自然地理学4版知识点总结课后答案
- 110kv变电站电气主接线设计资料全
- 华中科技大学版五年级信息技术教案
评论
0/150
提交评论