Webpack 如何解析模塊路徑

你一定見(jiàn)過(guò)這些導(dǎo)入方式,無(wú)論是 ESM 還是 CommonJS 模塊龟劲,或是其他模塊規(guī)范胃夏。

import react from 'react'
import button from './components/button'
const path = require('path')

那么 webpack 是如何去解析查找它們的呢?

模塊解析

resolver 是一個(gè)庫(kù)(library)昌跌,用于幫助找到模塊的絕對(duì)路徑仰禀。一個(gè)模塊可以作為另一個(gè)模塊的依賴(lài)模塊,然后被后者引用避矢。例如:

import foo from 'path/to/module'

所依賴(lài)的模塊可以是來(lái)自應(yīng)用程序或者第三方庫(kù)(library)悼瘾。resolver 幫助 webpack 找到 bundle 中需要引入的模塊代碼,這些代碼在每個(gè) import/require 語(yǔ)句中审胸。

webpack 使用 enhanced-resolve 來(lái)解析文件路徑亥宿。

解析規(guī)則

使用 enhanced-resolve 解析模塊,支持三種形式:絕對(duì)路徑砂沛、相對(duì)路徑烫扼、模塊路徑

1. 絕對(duì)路徑

不建議使用碍庵。

由于已經(jīng)取得文件的絕對(duì)路徑映企,因此不需要進(jìn)一步再做解析了。

在實(shí)際項(xiàng)目中静浴,除了設(shè)置別名 resolve.alias 時(shí)采用絕對(duì)路徑的方式堰氓,其他的我?guī)缀鯖](méi)見(jiàn)過(guò)使用絕對(duì)路徑的。(也可能我讀的項(xiàng)目太少了)

import button from '/Users/frankie/component/button'
2. 相對(duì)路徑

在這種情況下苹享,使用 import/require 的資源文件(resource file)所在的目錄被認(rèn)為是上下文目錄(context directory)双絮。在 import/require 中給定的相對(duì)路徑,會(huì)添加此上下文路徑(context path),以產(chǎn)生模塊的絕對(duì)路徑(absolute path)囤攀。

import button from './component/button'
3. 模塊路徑

上面兩種方式软免,應(yīng)該沒(méi)有太多理解難度,而模塊名才是我們要重點(diǎn)理解的焚挠。

直接引入模塊名膏萧,首先查找當(dāng)前文件目錄,若查找不到蝌衔,會(huì)繼續(xù)往父級(jí)目錄一個(gè)一個(gè)地查找榛泛,直至到項(xiàng)目根目錄下的 node_modules 目錄(默認(rèn))。若再查找不到胚委,則會(huì)拋出錯(cuò)誤挟鸠。

import 'react'
import 'module/lib/file'

注意:

  • 默認(rèn)的 node_modules 可以通過(guò) resolve.modules 進(jìn)行更改。
  • 查找中會(huì)根據(jù) resolve.extensions 自動(dòng)補(bǔ)全擴(kuò)展名亩冬,默認(rèn)是 ['.wasm', '.mjs', '.js', '.json']
  • 查找中會(huì)根據(jù) resolve.alias 替換掉別名硼身。

模塊將在 resolve.modules 中指定的目錄內(nèi)搜索硅急。可以通過(guò) resolve.alias 配置創(chuàng)建一個(gè)別名來(lái)替換初始模塊路徑佳遂。

一旦上述規(guī)則解析路徑之后营袜,解析器(resolver)將檢查路徑是否指向文件目錄

  • 指向文件

    1. 如果路徑具有文件擴(kuò)展名丑罪,則被直接打包荚板。
    2. 否則,將使用 resolve.extensions 選項(xiàng)作為文件擴(kuò)展名來(lái)解析吩屹。
  • 指向目錄

    按以下步驟找到具有正確擴(kuò)展名的文件:

    1. 如果文件夾中包含 package.json 文件跪另,則按順序查找 resolve.mainFields 配置選項(xiàng)中指定的字段,并且 package.json 中的第一個(gè)這樣的字段確定文件路徑煤搜。
    2. 如果 package.json 文件不存在或者 package.json 文件中 main 字段沒(méi)有返回一個(gè)有效路徑免绿,則按順序查找 resolve.mainFields 配置選項(xiàng)中指定的文件名,看是否能在 import/require 目錄下匹配到一個(gè)存在的文件名擦盾。
    3. 文件擴(kuò)展名通過(guò) resolve.extensions 選項(xiàng)采用類(lèi)似的方法進(jìn)行解析嘲驾。

若使用 webpack-dev-server 3.x 版本,建議不要隨意修改 resolve.mainFields 配置項(xiàng)迹卢,它會(huì)報(bào)錯(cuò)辽故。已確認(rèn)是 webpack-dev-server 的 bug,將在不久要發(fā)布的 4.x 版本修復(fù)腐碱。詳請(qǐng) #2801

解析與緩存

Loader 解析遵循與文件解析器指定的規(guī)則相同的規(guī)則誊垢。resolveLoader 配置選項(xiàng)可以用來(lái)為 Loader 提供獨(dú)立的解析規(guī)則。

每個(gè)文件系統(tǒng)訪問(wèn)都被緩存,以便更快觸發(fā)對(duì)同一文件的多個(gè)并行或者串行請(qǐng)求彤枢。在觀察模式下狰晚,只有修改過(guò)的文件會(huì)從緩存中摘出。如果關(guān)閉觀察模式缴啡,在每次編譯前清理緩存壁晒。

Resolve 配置

該選項(xiàng)用于配置模塊如何解析。例如业栅,當(dāng)在 ES6 中調(diào)用 import 'lodash'秒咐,resolve 選項(xiàng)能夠?qū)?webpack 查找 lodash 的方式去做修改。

1. resolve.alias

創(chuàng)建 import 或 require 的別名碘裕,來(lái)確保模塊引入變得更簡(jiǎn)單携取。

例如,一些位于 src/ 文件夾下的常用模塊:

// webpack.config.js
const path = require('path')

module.exports = {
  //...
  resolve: {
    alias: {
      // 可以是絕對(duì)路徑帮孔,或者是相對(duì)路徑雷滋。
      // 據(jù)我不完全觀察,結(jié)合 path 模塊和 __dirname 拼接成“絕對(duì)路徑”的方案更多文兢。
      // 以下為模糊匹配
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Templates: path.resolve(__dirname, 'src/templates/')
    }
  }
}

現(xiàn)在晤斩,你可以這樣使用別名了:

import Utility from '../../utilities/utility'

// 別名
import Utility from 'Utilities/utility'

也可以在給定的對(duì)象的鍵后的末尾添加 $,以表示精準(zhǔn)匹配姆坚。這里不展開(kāi)贅述澳泵,詳細(xì)請(qǐng)看這里

注意兼呵,采用別名引入模塊時(shí)兔辅,先替換后解析。先將模塊路徑中匹配 alias 中的 key 替換成對(duì)應(yīng)的 value击喂,再做查找维苔。

2. resolve.extensions

自動(dòng)解析確定的擴(kuò)展。

// webpack.config.js
module.exports = {
  //...
  resolve: {
    // 使用此選項(xiàng)茫负,會(huì)覆蓋默認(rèn)數(shù)組蕉鸳,默認(rèn)值:['.wasm', '.mjs', '.js', '.json']。
    // 注意不要少了符號(hào)(.)忍法,有些人配置不成功潮尝,就是因?yàn)樯倭怂?    // 從左到右(從上到下)先后匹配擴(kuò)展名,選項(xiàng)中沒(méi)有的后綴饿序,是不會(huì)自動(dòng)補(bǔ)全的勉失。
    extensions: ['.js', '.json']
  }
}
3. resolve.modules

告訴 webpack 解析模塊時(shí)應(yīng)該搜索的目錄≡剑可以是絕對(duì)路徑或者相對(duì)路徑乱凿,但是它們之間有一點(diǎn)差異顽素。

通過(guò)查看當(dāng)前目錄以及祖先路徑(即 ./node_modules../node_modules 等等)徒蟆,相對(duì)路徑將類(lèi)似于 Node 查找 node_modules 的方式進(jìn)行查找胁出。

當(dāng)使用絕對(duì)路徑,將只在給定目錄中搜索段审。

// webpack.config.js
const path = require('path')

module.exports = {
  //...
  resolve: {
    // 默認(rèn)值
    modules: ['node_modules']
    // 添加一個(gè)目錄到模塊搜索目錄全蝶,此目錄優(yōu)先于 node_modules 搜索。
    modules: [path.resolve(__dirname, 'src'), 'node_modules']
  }
}

一般地寺枉,不要去更改該選項(xiàng)抑淫。

4. resolve.mainFields

當(dāng)從 npm 包中導(dǎo)入模塊時(shí)(例如,import * as D3 from 'd3')姥闪,此選項(xiàng)將決定在 package.json 中使用哪個(gè)字段導(dǎo)入模塊始苇。根據(jù) webpack 配置中指定的 target 不同,默認(rèn)值也會(huì)有所不同筐喳。

// webpack.config.js
module.exports = {
  //...
  resolve: {
    // 不建議修改

    // target 為 webworker催式、web 或沒(méi)有指定時(shí),默認(rèn)值為:
    mainFields: ['browser', 'module', 'main'],

    // 除去上述幾個(gè) target疏唾,對(duì)于其他任意 target(包括 node)蓄氧,默認(rèn)值為:
    mainFields: ['browser', 'module', 'main']
  }
}

通常情況下,模塊的 package.json 都不會(huì)聲明 browsermodule 字段槐脏,所以便是使用 main 了。(該選項(xiàng)同樣不建議更改)

5. resolve.mainFiles

解析目錄時(shí)要使用的文件名撇寞。

當(dāng)目錄中沒(méi)有 package.json 時(shí)顿天,結(jié)合 resolve.extensions 來(lái)指明使用該目錄中哪個(gè)文件。

// webpack.config.js
module.exports = {
  //...
  resolve: {
    // 默認(rèn)值
    // 可添加多個(gè)蔑担,但不建議修改牌废。
    mainFiles: ['index']
  }
}

盡可能地,不要去修改該選項(xiàng)啤握。因?yàn)樗瑯訒?huì)影響第三方依賴(lài)包解析鸟缕,可能會(huì)導(dǎo)致部分第三方包解析錯(cuò)誤。例如排抬,我在驗(yàn)證該配置時(shí)懂从,就發(fā)現(xiàn) webpack-dev-server v3 的一個(gè) bug,開(kāi)發(fā)者表示將在 v4 版本中修復(fù)蹲蒲。

所以番甩,不建議隨意修改的配置包括 modulesmainFields届搁、mainFiles缘薛。

6. 更多

它還有其他一些配置項(xiàng)窍育,但比較少用,所以不展開(kāi)贅述宴胧。更多請(qǐng)看這里漱抓。

ResolveLoader 配置

從 webpack 2 開(kāi)始,在配置 loader 時(shí)強(qiáng)烈建議使用全名恕齐。例如 example-loader乞娄,以盡可能地清晰。

然而檐迟,如果你確實(shí)想省略 -loader补胚,也就是說(shuō)只使用 example,則可以使用 resolveLoader.moduleExtensions 此選項(xiàng)來(lái)實(shí)現(xiàn):

// webpack.config.js
module.exports = {
  //...
  resolve: {
    // ...
  }
  resolveLoader: {
    moduleExtensions: ['-loader']
  }
}

我使用 webpack 4 在不配置該選項(xiàng)時(shí)追迟,假如將 css-loader 省略為 css溶其,會(huì)報(bào)錯(cuò)提示找不到 loader。為什么我會(huì)單獨(dú)拿出來(lái)介紹一下敦间,因?yàn)榫W(wǎng)上很多文章表示在配置 module.rules 時(shí)可以省略 -loader瓶逃,但我是省略了就不行。所以這里補(bǔ)充一下原因廓块。

小技巧

關(guān)于 webpack 默認(rèn)配置可以從 node_modules/webpack/lib/WebpackOptionsDefaulter.js 查看厢绝。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市带猴,隨后出現(xiàn)的幾起案子昔汉,更是在濱河造成了極大的恐慌,老刑警劉巖拴清,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件靶病,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡口予,警方通過(guò)查閱死者的電腦和手機(jī)娄周,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沪停,“玉大人煤辨,你說(shuō)我怎么就攤上這事∧菊牛” “怎么了众辨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)窟哺。 經(jīng)常有香客問(wèn)我泻轰,道長(zhǎng),這世上最難降的妖魔是什么且轨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任浮声,我火速辦了婚禮虚婿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泳挥。我一直安慰自己然痊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布屉符。 她就那樣靜靜地躺著剧浸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矗钟。 梳的紋絲不亂的頭發(fā)上唆香,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音吨艇,去河邊找鬼躬它。 笑死,一個(gè)胖子當(dāng)著我的面吹牛东涡,可吹牛的內(nèi)容都是我干的冯吓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼疮跑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼组贺!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起祖娘,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤失尖,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后渐苏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雹仿,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年整以,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峻仇。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡公黑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出摄咆,到底是詐尸還是另有隱情凡蚜,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布吭从,位于F島的核電站朝蜘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏涩金。R本人自食惡果不足惜谱醇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一暇仲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧副渴,春花似錦奈附、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至勉盅,卻和暖如春佑颇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背草娜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工挑胸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驱还。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓嗜暴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親议蟆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闷沥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355