Create React App(以下簡稱 CRA)是建立 React 應用的一個腳手架,它與其他腳手架不同的一個地方就是將一些複雜工具(比如 webpack)的配置封裝了起來,讓使用者不用關心這些工具的具體配置,從而降低了工具的使用難度。
但是對於一些熟悉 webpack 的開發者來說,他們可能想對 webpack 配置做一些修改,這個時候應該怎麼辦呢?
其實我們可以通過以下幾種方式來修改 webpack 的配置:
- 專案 eject
- 替換 react-scripts 包
- 使用 react-app-rewired
- scripts 包 + override 組合
下面對這幾種方式分別進行介紹。
專案 eject
使用 CRA 建立完專案以後,專案在package.json
裡面提供了這樣一個命令:
{
...
"scripts": {
"eject": "react-scripts eject"
},
...
}
複製程式碼
執行完這個命令——yarn run eject
後會將封裝在 CRA 中的配置全部反編譯
到當前專案,這樣使用者就可以完全取得 webpack 檔案的控制權,想怎麼修改就怎麼修改了。
# eject 後專案根目錄下會出現 config 資料夾,裡面就包含了 webpack 配置
config
├── env.js
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── polyfills.js
├── webpack.config.dev.js // 開發環境配置
├── webpack.config.prod.js // 生產環境配置
└── webpackDevServer.config.js
複製程式碼
CRA 與其他腳手架不同的另一個地方,就是可以通過升級其中的react-scripts
包來升級 CRA 的特性。比如用老版本 CRA 建立了一個專案,這個專案不具備 PWA 功能,但只要專案升級了react-scripts
包的版本就可以具備 PWA 的功能,專案本身的程式碼不需要做任何修改。
但如果我們使用了eject
命令,就再也享受不到 CRA 升級帶來的好處了,因為react-scripts
已經是以檔案的形式存在於你的專案,而不是以包的形式,所以無法對其升級。
替換 react-scripts 包
react-scripts 是 CRA 的一個核心包,一些指令碼和工具的預設配置都整合在裡面,使用 CRA 建立專案預設就是使用這個包,但是 CRA 還提供了另外一種方式來建立 CRA 專案,即使用自定義 scripts 包的方式。
# 預設方式
$ create-react-app foo
# 自定義 scripts 包方式
$ create-react-app foo --scripts-version 自定義包
複製程式碼
自定義包
可以是下面幾種形式:
react-scripts
包的版本號,比如0.8.2
,這種形式可以用來安裝低版本的react-scripts
包。- 一個已經發布到 npm 倉庫上的包的名字,比如
your-scripts
,裡面包含了修改過的 webpack 配置。 - 一個 tgz 格式的壓縮檔案,比如
/your/local/scripts.tgz
,通常是未釋出到 npm 倉庫的自定義 scripts 包,可以用npm pack
命令生成。
這種方式相對於之前的eject
是一種更靈活地修改 webpack 配置的方式,而且可以做到和 CRA 一樣,通過升級 scrips 包來升級專案特性。
自定義 scripts 包的結構可以參照react-scripts
包的結構,只要修改對應的 webpack 配置檔案,並安裝上所需的 webpack loader 或 plugin 包就可以了。
使用 react-app-rewired
雖然有這兩種方式可以擴充套件 webpack 配置,但是很多開發者還是覺得太麻煩,有沒有一種方式可以既不用eject
專案又不用建立自己的 scripts 包呢?答案是肯定的,react-app-rewired 是 react 社群開源的一個修改 CRA 配置的工具。
在 CRA 建立的專案中安裝了react-app-rewired
後,可以通過建立一個config-overrides.js
檔案來對 webpack 配置進行擴充套件。
/* config-overrides.js */
module.exports = function override(config, env) {
//do stuff with the webpack config...
return config;
}
複製程式碼
override
方法的第一個引數config
就是 webpack 的配置,在這個方法裡面,我們可以對 config
進行擴充套件,比如安裝其他 loader 或者 plugins,最後再將這個 config
物件返回回去。
最後再修改package.json
中的指令碼命令,修改內容請見這裡。
scripts 包 + override 組合
雖然react-app-rewired
的方式已經可以很方便地修改 webpack 的配置了,但其實我們也可以在自定義的 script 包中實現類似的功能。
在react-app-rewired
的原始碼中可以看到它核心的包也叫 react-app-rewired,裡面重新覆蓋了react-scripts
中的幾個指令碼檔案,包括build.js
、start.js
和test.js
。
具體過程是怎樣的呢?以build.js
為例:
- 先獲取 webpack 的基本配置,然後再呼叫
config-overrides.js
(就是在根目錄中新增的那個檔案)中的override
方法,將原先的 webpack 物件作為引數傳入, - 再取得經過修改後的 webpack 配置物件
- 最後再呼叫
react-scripts
中的build.js
指令碼,傳入修改後的 webpack 物件來執行命令,
具體原始碼如下:
const overrides = require('../config-overrides');
const webpackConfigPath = paths.scriptVersion + "/config/webpack.config.prod";
// load original config
const webpackConfig = require(webpackConfigPath);
// override config in memory
require.cache[require.resolve(webpackConfigPath)].exports =
overrides.webpack(webpackConfig, process.env.NODE_ENV);
// run original script
require(paths.scriptVersion + '/scripts/build');
複製程式碼
知道了原理之後,我們也可以修改自定義 scripts 包的指令碼檔案,還是以build.js
為例,在獲取基本 webpack 配置物件和使用 webpack 物件之間加入以下程式碼:
// override config
const override = require(paths.configOverrides);
const overrideFn = override || ((config, env) => config);
const overrideConfig = overrideFn(config, process.env.NODE_ENV);
複製程式碼
overrideConfig
就是修改後的 webpack 物件,最後修改呼叫了 webpack 物件的程式碼,將原來的 webpack 物件替換成修改後的 webpack 物件。
總結
CRA 是一個非常棒的 React 腳手架工具,但你如果不滿足於它的 webpack 預設配置,你可以通過上述幾種方式來擴充套件自己專案的 webpack 配置,這幾種方式各有優缺點,可以結合具體的使用場景來選擇合適自己的方式。
關注微信公眾號:創宇前端(KnownsecFED),碼上獲取更多優質乾貨!