前奏曲
眾所周知, create-react-app
(以下稱為CRA)是 FaceBook 開源的 建立 React
現代Web應用程式應用的腳手架,讓我們可以快速和專注專案的開發而不用過多的去關心工具和服務的配置,在一些場景中還是需要另外的加入或者修改 CRA 中的配置,在 CRA 的官方文件中指出可以通過執行npm run eject
來暴露出配置檔案來進行修改,但是這種方式是不可逆的,同時會有一些隱藏的問題。
eject做了什麼
當你執行 npm run eject
會將潛藏的一系列配置檔案和一些依賴項都“彈出”到專案中,然後就可以由你自己完全控制,但是這個過程是不可逆的。
來看看 npm run eject
後專案發生了什麼變化,以下的示例使用 npx create-react-app
進行建立。
eject之前的目錄結構:
eject之後的目錄結構:
通過
Git
版本管理追蹤到的檔名高亮顯示可以看出eject
後產生變化,而開啟package.json
可以看到專案的依賴項(dependencies
)和npm script
發生了變化以及新增了 jest
的配置。eject副作用
雖然Eject後可以根據自己的需要新增配置項做一些更高階的配置,但是由於這個操作是不可逆的,所以也會帶來一些問題:
無法迎接未來
如果後續的create-react-app
更新了並新增了很多不錯的功能,你想應用到你的專案中,可以通過更新 create-react-app
版本來實現,而不是去更新webpack的配置,但是如果已經對專案進行了 eject
操作,那麼你無法很好的“迎接”未來。
程式碼潔癖
create-react-app
其中的一個目的就是可以讓你專注與專案的開發,這對於一些有程式碼潔癖或者喜歡保持應用程式儘可能乾淨的人是友好的,他不用去關心其他的配置與服務,看不到其他從來不用關心的程式碼,然而在 eject
之後,package.json
就寫入了很多依賴項,還有其他的指令碼和配置檔案,差不多有10多個新檔案,每個檔案包含了50-200行程式碼,這對於這類人在維護依賴項的時候會辣眼睛。
回到根本
在 eject
修改配置後,你會將其複製貼上到其他的專案中,因為在大多數情況下,專案的配置(比如webpack與babel)都是相同的。如果你由多個類似的專案,那麼你必須得自己去維護多份配置和指令碼,告辭!
eject前請三思
在你 eject
之前請多思考和對你的問題或者疑問進行調查。
Eject後自己新增補充的功能是CRA缺少的嗎?
CRA 缺少所需要的功能,需要自己去新增,這其實是大部分人進行 eject
的原因。但是在 eject
之前,請你花點時間思考一下或者搜尋一下來確保自己需要新增的功能不會變成額外的工作或者重複別人之前已經做過的工作。
CRA的 issues
中已經記錄了很多的問題,很有可能已經有人解決了你的問題,去在討論區裡探索一下,你有可能會發現解決方案或者替代的方法,如果沒有關於你問題的相關討論,你可以考慮提出 issues
,而且經過深思的 issues
是值得被讚賞的。
Eject給你帶來多少價值?
在 eject
修改配置檔案之前,你可以問問自己,如果進行這個更改可以增加多少價值?增加價值是否超過了管理構建過程中所引入的認知負擔?
在執行eject
後,你得對更新那些你可能沒有完全瞭解的程式碼負責,因為 eject
後將新增數千行復雜的程式碼進行構建和測試,你需要學習這些程式碼來正確的更新、測試和構建。如果構建不通過,CRA 也將無法支援你的自定義配置,如果你有一個開發團隊,你是否相信他們還需要充分了解他們對構建過程所做的更改來保證其穩定性。
需要對CRA進行很大更改?
如果你的工作流是很特殊的,需要特殊的工具或者服務,但是在你 eject
之前,請思考讓你的用例比你想象中的更加重用,fork react-script
允許你進行更改,還可以在其他類似的專案上進行重用,而不是 eject
瞭然後自己去維護。這種方式相對於直接進行 eject
是一種更靈活和維護性更強的一種修改方式,可以和CRA一樣進行升級script包來應用特性。
允許其他人使用併為你的構建過程做出貢獻,這可以提高專案的穩定性和完善性。Fork 也是CRA團隊所建議的,這篇文件提供了指南來引導你如何將 create-react-app
定製化為自己的利器。
想學習CRA內部構建過程的原理?
這是極好的!如果你想學習內部的工作原理,那麼請盡情的進行 eject
!不過了解CRA的工作原理是一項不簡單的任務,如果你感到困惑或者迷茫,請不要灰心,多在社群、google、stackoverflow上進行探索,你不會辜負你所付出的!
替代方案
react-app-rewired + customize-cra
文件連線:
在專案中使用過 ant design
的應該知道,可以使用react-app-rewired
和 customize-cra
這兩個工具庫來進行自定義配置。
對於 create-react-app1.x
需要使用 react-app-rewired@1.62
,而因為各自版本升級的原因,在到 react-app-rewired@2.x
版本的時候只保留了核心的功能,在這個commit你可以看到移除了 rewire helper
的功能函式。因此你需要藉助customize-cra
來自定義 CRA v2+以上的配置。
常見用法
首先安裝依賴:
yarn add react-app-rewired customize-cra
複製程式碼
antd 配置:
修改npm script:
"start": "PROT=3002 react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
複製程式碼
專案根目錄建立 config-override.js
並寫入配置:
// confit-override.js
// 按需載入元件程式碼和樣式
// addLessLoader 來幫助載入 less 樣式,幫助自定義主題
// 使用外掛讓 Day.js 替換 momentjs 減小打包大小,
const { override, fixBabelImports,addWebpackPlugin, addLessLoader } = require('customize-cra');
const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
const path = require('path');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
}),
addLessLoader({
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
}),
addWebpackPlugin(new AntdDayjsWebpackPlugin())
);
複製程式碼
新增alias別名
在平時專案開發中,經常性會遇見引用元件或者工具函式時會出現這樣的情況:
import header from '../../../../components/header';
複製程式碼
而alias別名的設定,會讓這更為方便的引用。
// config-override.js
const { addWebpackAlias} = require('customize-cra');
const resolve = dir => path.join(__dirname, '.', dir);
module.exports = override(
...
addWebpackAlias({
['src']: resolve('./src')
}),
...
)
複製程式碼
如果是TS專案,注意需要在 tsconfig.json
中進一步設定,如果直接在 tsconfig.json
檔案中直接設定 paths
屬性,當重新run
的時候,屬性又會被刪除。
CRA issue 中的解決方式:
github.com/facebook/cr…
// tsconfig.json
{
...
"extends": "./paths.json",
...
}
複製程式碼
// paths.json
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"src/*": ["*"]
}
}
}
複製程式碼
API代理
const {
override,
overrideDevServer,
} = require('customize-cra');
const devServerConfig = () => config => {
return {
...config,
port: 3000,
proxy: {
'/app/v1': {
target: 'http://localhost:3005',
changeOrigin: true,
ws: false,
pathRewrite: {
'^/app/v1': '/app/v1',
},
secure: false,
},
},
}
}
module.exports = {
...
devServer: overrideDevServer(
devServerConfig()
),
...
}
複製程式碼
關閉sourceMap
//config-override.js
const {
override,
} = require('customize-cra');
const rewiredMap = () => config => {
config.devtool = config.mode === 'development' ? 'cheap-module-source-map' : false;
return config;
};
module.exports = override(
rewiredMap()
);
複製程式碼
其他更多用法可參見文件,傳送門。
@craco/craco
首先安裝依賴:
yarn add @craco/craco
複製程式碼
同樣修改 npm scripts
:
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
}
複製程式碼
常見用法
修改alias別名
根目錄建立craco.config.js
並寫入配置
//craco.config.js
const path = require('path');
const resolve = dir => path.join(__dirname, '.', dir);
module.exports = {
...
webpack: {
alias: {
'src': resolve('src')
}
}
...
}
複製程式碼
antd配置
安裝 craco-antd
外掛
yarn add craco-antd
複製程式碼
新增配置
const CracoAntDesignPlugin = require("craco-antd");
module.exports = {
...
plugins: [{ plugin: CracoAntDesignPlugin }],
...
}
複製程式碼
其他更多用法可參見文件,傳送門。
最後
現在我們知道有哪些方式來修改CRA建立的專案中的預設配置,不管是 npm run eject
或者是通過 fork
一份 react-script
來正對專案來高度定製化,在或者是用一些替代的方法都是可以達到目的的,不過從對比上來看,使用 craco
或者 react-app-rewired
+ customize-cra
是可以滿足絕大多數需求的,不過各自帶來的效應不管是負面還是正面的,既然去使用來就得去面對。
記得在你進行 eject
之前,請三思!多思考思考!你的專案和現有團隊的專案中需要修改CRA中預設配置的需求到底是怎麼樣的!選擇合適的,才是最好的!
人生也是,多思考,多看看自己內心深處到底需要的是什麼。