Create React App(如下簡稱 CRA)是建立 React 應用的一個腳手架,它與其餘腳手架不一樣的一個地方就是將一些複雜工具(好比 webpack)的配置封裝了起來,讓使用者不用關心這些工具的具體配置,從而下降了工具的使用難度。css
可是對於一些熟悉 webpack 的開發者來講,他們可能想對 webpack 配置作一些修改,這個時候應該怎麼辦呢?react
其實咱們能夠經過如下幾種方式來修改 webpack 的配置:webpack
- 項目 eject
- 替換 react-scripts 包
- 使用 react-app-rewired
- scripts 包 + override 組合
下面對這幾種方式分別進行介紹。git
項目 eject
使用 CRA 建立完項目之後,項目在package.json
裏面提供了這樣一個命令:github
1
2 3 4 5 6 7 |
{ ... "scripts": { "eject": "react-scripts eject" }, ... } |
執行完這個命令——yarn run eject
後會將封裝在 CRA 中的配置所有反編譯
到當前項目,這樣用戶就能夠徹底取得 webpack 文件的控制權,想怎麼修改就怎麼修改了。web
1
2 3 4 5 6 7 8 9 10 11 |
# 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 的功能,項目自己的代碼不須要作任何修改。npm
但若是咱們使用了eject
命令,就再也享受不到 CRA 升級帶來的好處了,由於react-scripts
已是以文件的形式存在於你的項目,而不是以包的形式,因此沒法對其升級。json
替換 react-scripts 包
react-scripts 是 CRA 的一個核心包,一些腳本和工具的默認配置都集成在裏面,使用 CRA 建立項目默認就是使用這個包,可是 CRA 還提供了另一種方式來建立 CRA 項目,即便用自定義 scripts 包的方式。app
1
2 3 4 5 |
# 默認方式 $ create-react-app foo # 自定義 scripts 包方式 $ create-react-app foo --scripts-version 自定義包 |
自定義包
能夠是下面幾種形式:ide
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 配置進行擴展。
1
2 3 4 5 6 |
/* 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 對象來執行命令,
具體源碼以下:
1
2 3 4 5 6 7 8 9 10 |
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 對象之間加入如下代碼:
1
2 3 4 |
// 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 配置,這幾種方式各有優缺點,能夠結合具體的使用場景來選擇合適本身的方式。