研究了 babel.config.js 和 babelrc,理解了為什么ES6代碼沒被轉(zhuǎn)化

前言
之前遇到過一個 babel 轉(zhuǎn)換的問題,在下面這個 monorepo 的目錄結(jié)構(gòu)中
.
├── common
│   └── utils.js
├── package.json
└── packages
    └── modulea
        ├── .babelrc
        ├── index.js
        ├── package.json
        └── webpack.config.js

3 directories, 6 files
modulea 引用了外層 common/utils.js,結(jié)果在打包時發(fā)現(xiàn)common/utils.js 的代碼并沒有被翻譯,但是 modulea 目錄內(nèi)的代碼卻能正常轉(zhuǎn)換,覺得很奇怪,直到我將 babel 的配置文件由 .babelrc 改成了 babel.config.js 才能正常工作。于是就深究了一下兩個配置文件的區(qū)別,覺得有點東西理解起來有點繞,于是寫下這篇博客記錄一下。

babel 7.x 以上開始支持兩種類型的配置文件, 分別是.babelrc 和 babel.config.js ,在官方的描述里:

babel.config.js 當(dāng)前項目維度 (Project Wide)的配置文件,相當(dāng)于一份全局配置,如果 babel 決定應(yīng)用這個配置文件,則一定會應(yīng)用到所有文件的轉(zhuǎn)換。

.babelrc 相對文件(File Relative)的配置文件, babel 決定一個 js 文件應(yīng)用哪些配置文件時,會執(zhí)行如下策略: 如果要轉(zhuǎn)換的這個 js 文件在當(dāng)前項目內(nèi),則會先遞歸向上搜索最近的一個 .babelrc 文件(直到遇到package.json目錄),將其與全局配置合并。如果這個 js 文件不在當(dāng)前項目內(nèi),則只應(yīng)用全局配置,忽略與這個文件相關(guān)的 .babelrc 。這兩個我更愿意將其稱為全局配置 (babel.config.js) 和局部配置 (.babelrc) 便于理解一些。

由此便引入了一個非常關(guān)鍵的對 當(dāng)前項目 的定義問題。

這個定義在官方文檔就有說明

New in Babel 7.x, Babel has a concept of a "root" directory, which defaults to the current working directory. For project-wide configuration, Babel will automatically search for a babel.config.json file, or an equivalent one using the supported extensions, in this root directory.

大白話就是: 當(dāng)前所在目錄即為*當(dāng)前項目*根目錄,babel 會讀取當(dāng)前所在目錄的 babel.config.js 作為全局配置。

下面我們分為幾種 case 分別看一下 babel 的應(yīng)用路徑.

Case 1 所有代碼都在一個單一工程中
這種情況下 babel.config.js 和 .babelrc 作用幾乎一樣,唯一不同是 babelrc 可以針對子目錄分別配置.

.
├── .babelrc
├── babel.config.js
├── package.json
└── src
    ├── index.js
    └── subdir
        ├── .babelrc
        └── utils.js

2 directories, 6 files
這里, 我們在頂層目錄運行 babel src/**/*.js --out-dir dist 。此時會讀取當(dāng)前目錄的 babel.config.js 作為全局配置,根據(jù)前文所述規(guī)則應(yīng)用的配置如

src/index.js 會應(yīng)用 babel.config.js + .babelrc。
src/subdir/utils.js 會應(yīng)用 babel.config.js + src/subdir/.babelrc
如果我們在 src 目錄執(zhí)行 babel **/*.js --outdir ,此時則不會應(yīng)用 babel.config.js 的規(guī)則,但會應(yīng)用 src 外層 .babelrc 的配置文件。

此時:

index.js 會應(yīng)用 ../.babelrc
subdir/index.js 會應(yīng)用 subdir/.babelrc





這里貌似有點不對勁, index.js 為什么會應(yīng)用外層 .babelrc , 這個配置文件明明在當(dāng)前項目"外"。

這里再回顧一下之前說的。

babel 決定一個 js 文件應(yīng)用哪些配置文件時,會執(zhí)行如下策略: 如果這個 js 文件在當(dāng)前項目內(nèi),則會先遞歸向上搜索最近的一個 .babelrc 文件(直到遇到package.json目錄),將其與全局配置合并。如果這個 js 文件不在當(dāng)前項目內(nèi),則只應(yīng)用全局配置,并忽略與這個文件相關(guān)的 .babelrc 。

babel決定是否執(zhí)行向上搜索 .babelrc 這個策略時依據(jù)的是當(dāng)前 js 文件的位置,而不是 .babelrc 的位置。此時的 index.js 在項目范圍內(nèi),所以會執(zhí)行向上搜索 .babelrc 的策略。

Case 2 多工程下的項目配置問題
對于 monorepo 項目,全局配置文件就有用的多了。首先看我開頭遇到的問題

.
├── common
│   └── utils.js
├── package.json
└── packages
    └── modulea
        ├── .babelrc
        ├── index.js
        ├── package.json
        └── webpack.config.js

3 directories, 6 files
這里我在 packages/modulea 目錄里執(zhí)行打包命令時, .babelrc 只會應(yīng)用 packages/modulea 里的文件,對于 common 目錄下的文件則無法執(zhí)行轉(zhuǎn)換。即便我們在 common 目錄中添加一個 .babelrc 也無濟于事,因為 common 里的代碼在當(dāng)前的項目范圍外面,只會應(yīng)用全局配置。所以這種場景就非常適合使用 babel.config.js 。
.
├── common
│   └── utils.js
├── package.json
└── packages
    └── modulea
        ├── babel.config.js <---
        ├── index.js
        ├── package.json
        └── webpack.config.js

3 directories, 6 files
改成 babel.config.js 之后 common 目錄就能正確進行轉(zhuǎn)換了。

為了清晰記憶上面的規(guī)則我來稍微總結(jié)一下:

babel 會在當(dāng)前執(zhí)行目錄搜索 babel.config.js, 若有則讀取并作為全局配置,若無則全局配置為空。然后在轉(zhuǎn)換一個具體的js文件時會去判斷,如果這個文件在當(dāng)前執(zhí)行目錄外面,則只應(yīng)用全局配置。如果這個文件在當(dāng)前執(zhí)行路徑內(nèi),則會去基于這個文件向上搜索最近的一個 .babelrc ,將其與全局配置合并作為轉(zhuǎn)換這個文件的配置。

作者:前端陽光


歡迎關(guān)注微信公眾號 :前端陽光