摘要:在一個構(gòu)建過程中,首先根據(jù)的依賴類型例如調(diào)用對應(yīng)的構(gòu)造函數(shù)來創(chuàng)建對應(yīng)的模塊。
文章首發(fā)于個人github blog: Biu-blog,歡迎大家關(guān)注~
Webpack 系列文章:
Webpack Loader 高手進(jìn)階(一)
Webpack Loader 高手進(jìn)階(二)
Webpack Loader 高手進(jìn)階(三)
Webpack 對于一個 module 所使用的 loader 對開發(fā)者提供了2種使用方式:
webpack config 配置形式,形如:
// webpack.config.js module.exports = { ... module: { rules: [{ test: /.vue$/, loader: "vue-loader" }, { test: /.scss$/, use: [ "vue-style-loader", "css-loader", { loader: "sass-loader", options: { data: "$color: red;" } } ] }] } ... }
inline 內(nèi)聯(lián)形式
// module import a from "raw-loader!../../utils.js"
2種不同的配置形式,在 webpack 內(nèi)部有著不同的解析方式。此外,不同的配置方式也決定了最終在實(shí)際加載 module 過程中不同 loader 之間相互的執(zhí)行順序等。
loader 的匹配在講 loader 的匹配過程之前,首先從整體上了解下 loader 在整個 webpack 的 workflow 過程中出現(xiàn)的時機(jī)。
在一個 module 構(gòu)建過程中,首先根據(jù) module 的依賴類型(例如 NormalModuleFactory)調(diào)用對應(yīng)的構(gòu)造函數(shù)來創(chuàng)建對應(yīng)的模塊。在創(chuàng)建模塊的過程中(new NormalModuleFactory()),會根據(jù)開發(fā)者的 webpack.config 當(dāng)中的 rules 以及 webpack 內(nèi)置的 rules 規(guī)則實(shí)例化 RuleSet 匹配實(shí)例,這個 RuleSet 實(shí)例在 loader 的匹配過濾過程中非常的關(guān)鍵,具體的源碼解析可參見Webpack Loader Ruleset 匹配規(guī)則解析。實(shí)例化 RuleSet 后,還會注冊2個鉤子函數(shù):
class NormalModuleFactory { ... // 內(nèi)部嵌套 resolver 的鉤子,完成相關(guān)的解析后,創(chuàng)建這個 normalModule this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => { ... }) // 在 hooks.factory 的鉤子內(nèi)部進(jìn)行調(diào)用,實(shí)際的作用為解析構(gòu)建一共 module 所需要的 loaders 及這個 module 的相關(guān)構(gòu)建信息(例如獲取 module 的 packge.json等) this.hooks.resolver.tap("NormalModuleFactory", () => (result, callback) => { ... }) ... }
當(dāng) NormalModuleFactory 實(shí)例化完成后,并在 compilation 內(nèi)部調(diào)用這個實(shí)例的 create 方法開始真實(shí)開始創(chuàng)建這個 normalModule。首先調(diào)用hooks.factory獲取對應(yīng)的鉤子函數(shù),接下來就調(diào)用 resolver 鉤子(hooks.resolver)進(jìn)入到了 resolve 的階段,在真正開始 resolve loader 之前,首先就是需要匹配過濾找到構(gòu)建這個 module 所需要使用的所有的 loaders。首先進(jìn)行的是對于 inline loaders 的處理:
// NormalModuleFactory.js // 是否忽略 preLoader 以及 normalLoader const noPreAutoLoaders = requestWithoutMatchResource.startsWith("-!"); // 是否忽略 normalLoader const noAutoLoaders = noPreAutoLoaders || requestWithoutMatchResource.startsWith("!"); // 忽略所有的 preLoader / normalLoader / postLoader const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith("!!"); // 首先解析出所需要的 loader,這種 loader 為內(nèi)聯(lián)的 loader let elements = requestWithoutMatchResource .replace(/^-?!+/, "") .replace(/!!+/g, "!") .split("!"); let resource = elements.pop(); // 獲取資源的路徑 elements = elements.map(identToLoaderRequest); // 獲取每個loader及對應(yīng)的options配置(將inline loader的寫法變更為module.rule的寫法)
首先是根據(jù)模塊的路徑規(guī)則,例如模塊的路徑是以這些符號開頭的 ! / -! / !! 來判斷這個模塊是否只是使用 inline loader,或者剔除掉 preLoader, postLoader 等規(guī)則:
! 忽略 webpack.config 配置當(dāng)中符合規(guī)則的 normalLoader
-! 忽略 webpack.config 配置當(dāng)中符合規(guī)則的 preLoader/normalLoader
!! 忽略 webpack.config 配置當(dāng)中符合規(guī)則的 postLoader/preLoader/normalLoader
這幾個匹配規(guī)則主要適用于在 webpack.config 已經(jīng)配置了對應(yīng)模塊使用的 loader,但是針對一些特殊的 module,你可能需要多帶帶的定制化的 loader 去處理,而不是走常規(guī)的配置,因此可以使用這些規(guī)則來進(jìn)行處理。
接下來將所有的 inline loader 轉(zhuǎn)化為數(shù)組的形式,例如:
import "style-loader!css-loader!stylus-loader?a=b!../../common.styl"
最終 inline loader 統(tǒng)一格式輸出為:
[{ loader: "style-loader", options: undefined }, { loader: "css-lodaer", options: undefined }, { loader: "stylus-loader", options: "?a=b" }]
對于 inline loader 的處理便是直接對其進(jìn)行 resolve,獲取對應(yīng) loader 的相關(guān)信息:
asyncLib.parallel([ callback => this.resolveRequestArray( contextInfo, context, elements, loaderResolver, callback ), callback => { // 對這個 module 進(jìn)行 resolve ... callack(null, { resouceResolveData, // 模塊的基礎(chǔ)信息,包含 descriptionFilePath / descriptionFileData 等(即 package.json 等信息) resource // 模塊的絕對路徑 }) } ], (err, results) => { const loaders = results[0] // 所有內(nèi)聯(lián)的 loaders const resourceResolveData = results[1].resourceResolveData; // 獲取模塊的基本信息 resource = results[1].resource; // 模塊的絕對路徑 ... // 接下來就要開始根據(jù)引入模塊的路徑開始匹配對應(yīng)的 loaders let resourcePath = matchResource !== undefined ? matchResource : resource; let resourceQuery = ""; const queryIndex = resourcePath.indexOf("?"); if (queryIndex >= 0) { resourceQuery = resourcePath.substr(queryIndex); resourcePath = resourcePath.substr(0, queryIndex); } // 獲取符合條件配置的 loader,具體的 ruleset 是如何匹配的請參見 ruleset 解析(https://github.com/CommanderXL/Biu-blog/issues/30) const result = this.ruleSet.exec({ resource: resourcePath, // module 的絕對路徑 realResource: matchResource !== undefined ? resource.replace(/?.*/, "") : resourcePath, resourceQuery, // module 路徑上所帶的 query 參數(shù) issuer: contextInfo.issuer, // 所解析的 module 的發(fā)布者 compiler: contextInfo.compiler }); // result 為最終根據(jù) module 的路徑及相關(guān)匹配規(guī)則過濾后得到的 loaders,為 webpack.config 進(jìn)行配置的 // 輸出的數(shù)據(jù)格式為: /* [{ type: "use", value: { loader: "vue-style-loader", options: {} }, enforce: undefined // 可選值還有 pre/post 分別為 pre-loader 和 post-loader }, { type: "use", value: { loader: "css-loader", options: {} }, enforce: undefined }, { type: "use", value: { loader: "stylus-loader", options: { data: "$color red" } }, enforce: undefined }] */ const settings = {}; const useLoadersPost = []; // post loader const useLoaders = []; // normal loader const useLoadersPre = []; // pre loader for (const r of result) { if (r.type === "use") { // postLoader if (r.enforce === "post" && !noPrePostAutoLoaders) { useLoadersPost.push(r.value); } else if ( r.enforce === "pre" && !noPreAutoLoaders && !noPrePostAutoLoaders ) { // preLoader useLoadersPre.push(r.value); } else if ( !r.enforce && !noAutoLoaders && !noPrePostAutoLoaders ) { // normal loader useLoaders.push(r.value); } } else if ( typeof r.value === "object" && r.value !== null && typeof settings[r.type] === "object" && settings[r.type] !== null ) { settings[r.type] = cachedMerge(settings[r.type], r.value); } else { settings[r.type] = r.value; } // 當(dāng)獲取到 webpack.config 當(dāng)中配置的 loader 后,再根據(jù) loader 的類型進(jìn)行分組(enforce 配置類型) // postLoader 存儲到 useLoaders 內(nèi)部 // preLoader 存儲到 usePreLoaders 內(nèi)部 // normalLoader 存儲到 useLoaders 內(nèi)部 // 這些分組最終會決定加載一個 module 時不同 loader 之間的調(diào)用順序 // 當(dāng)分組過程進(jìn)行完之后,即開始 loader 模塊的 resolve 過程 asyncLib.parallel([ [ // resolve postLoader this.resolveRequestArray.bind( this, contextInfo, this.context, useLoadersPost, loaderResolver ), // resove normal loaders this.resolveRequestArray.bind( this, contextInfo, this.context, useLoaders, loaderResolver ), // resolve preLoader this.resolveRequestArray.bind( this, contextInfo, this.context, useLoadersPre, loaderResolver ) ], (err, results) => { ... // results[0] -> postLoader // results[1] -> normalLoader // results[2] -> preLoader // 這里將構(gòu)建 module 需要的所有類型的 loaders 按照一定順序組合起來,對應(yīng)于: // [postLoader, inlineLoader, normalLoader, preLoader] // 最終 loader 所執(zhí)行的順序?qū)?yīng)為: preLoader -> normalLoader -> inlineLoader -> postLoader // 不同類型 loader 上的 pitch 方法執(zhí)行的順序?yàn)椋?postLoader.pitch -> inlineLoader.pitch -> normalLoader.pitch -> preLoader.pitch (具體loader內(nèi)部執(zhí)行的機(jī)制后文會多帶帶講解) loaders = results[0].concat(loaders, results[1], results[2]); process.nextTick(() => { ... // 執(zhí)行回調(diào),創(chuàng)建 module }) } ]) } })
簡單總結(jié)下匹配的流程就是:
首先處理 inlineLoaders,對其進(jìn)行解析,獲取對應(yīng)的 loader 模塊的信息,接下來利用 ruleset 實(shí)例上的匹配過濾方法對 webpack.config 中配置的相關(guān) loaders 進(jìn)行匹配過濾,獲取構(gòu)建這個 module 所需要的配置的的 loaders,并進(jìn)行解析,這個過程完成后,便進(jìn)行所有 loaders 的拼裝工作,并傳入創(chuàng)建 module 的回調(diào)中。
文章首發(fā)于個人github blog: Biu-blog,歡迎大家關(guān)注~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/109088.html
摘要:相關(guān)的內(nèi)容為這樣對于一個處理的第二階段也就結(jié)束了,通過去攔截不同類型的,并返回新的,跳過后面的的執(zhí)行,同時在內(nèi)部會剔除掉,這樣在進(jìn)入到下一個處理階段的時候,不在使用的范圍之內(nèi),因此下一階段便不會經(jīng)由來處理。 文章首發(fā)于個人github blog: Biu-blog,歡迎大家關(guān)注~ Webpack 系列文章: Webpack Loader 高手進(jìn)階(一)Webpack Loader 高手...
摘要:如果函數(shù)沒有返回值的話,那么進(jìn)入到下一個的函數(shù)的執(zhí)行階段。這也是異步化的一種方式如果執(zhí)行后有返回值,執(zhí)行開始下一個執(zhí)行以上就是對于在構(gòu)建過程中執(zhí)行流程的源碼分析。 文章首發(fā)于個人github blog: Biu-blog,歡迎大家關(guān)注~ Webpack 系列文章: Webpack Loader 高手進(jìn)階(一)Webpack Loader 高手進(jìn)階(二)Webpack Loader 高手...
webpack的loaders是一大特色,也是很重要的一部分。這遍博客我將分類講解一些常用的laodershowImg(https://segmentfault.com/img/remote/1460000005742040); 一、loaders之 預(yù)處理 css-loader 處理css中路徑引用等問題 style-loader 動態(tài)把樣式寫入css sass-loader scss編譯器 ...
摘要:,我想大家應(yīng)該都知道或者聽過,是前端一個工具可以讓各個模塊進(jìn)行加載預(yù)處理再進(jìn)行打包。 webpack,我想大家應(yīng)該都知道或者聽過,Webpack是前端一個工具,可以讓各個模塊進(jìn)行加載,預(yù)處理,再進(jìn)行打包。現(xiàn)代的前端開發(fā)很多環(huán)境都依賴webpack構(gòu)建,比如vue官方就推薦使用webpack.廢話不多說,我們趕緊開始吧. 第一步、安裝webpack 新建文件夾webpack->再在web...
摘要:配置文件中設(shè)置引入文件之前講過在引用文件的時候可以引入和,諸如這樣現(xiàn)在提供一種更常用和更便捷的方法。同樣先在項(xiàng)目文件夾下安裝和模塊配置文件如下選項(xiàng)用來處理對應(yīng)的模塊用正則匹配所有的文件使用模塊這樣就已經(jīng)可以正常打包文件了。 配置文件中設(shè)置引入css文件 之前講過在引用css文件的時候可以引入style-loader和css=loader,諸如這樣: require(style-load...
閱讀 3530·2021-11-23 10:10
閱讀 3291·2019-08-30 14:03
閱讀 2065·2019-08-30 13:09
閱讀 3392·2019-08-29 15:29
閱讀 1540·2019-08-29 11:23
閱讀 2002·2019-08-28 18:28
閱讀 2840·2019-08-26 13:34
閱讀 2167·2019-08-26 11:32