create-react-app 优雅定制指南
create-react-app
是一款广泛使用的脚手架,默认它只能使用 eject
命令暴露出 webpack
配置,其实这样使用很不优雅,修改内容文件的话也不利于维护, react-app-rewired
正式解决这样问题的工具,今天我们就好好学习下它的用法。
1. 安装 react-app-rewired
create-react-app 2.x with Webpack 4
npm install react-app-rewired --save-dev
create-react-app 1.x or react-scripts-ts with Webpack 3
npm install react-app-rewired@1.6.2 --save-dev
2. 根目录创建config-overrides.js
/* config-overrides.js */ module.exports = function override(config, env) { //do stuff with the webpack config... return config; }
当然我们也可以把 config-overrides.js
放到其他位置,比如我们要指向 node_modules
中某个第三方库提供的配置文件,就可以添加下面配置到 package.json
:
"config-overrides-path": "node_modules/other-rewire"
3. 替换 react-scripts
打开 package.json
:
/* package.json */ "scripts": { - "start": "react-scripts start", + "start": "react-app-rewired start", - "build": "react-scripts build", + "build": "react-app-rewired build", - "test": "react-scripts test --env=jsdom", + "test": "react-app-rewired test --env=jsdom", "eject": "react-scripts eject" }
4. 配置
定制 Webpack 配置
webpack
字段可以用来添加你的额外配置,当然这里面不包含 Webpack Dev Server
。
const { override, overrideDevServer, fixBabelImports, addLessLoader, addWebpackAlias, addWebpackModuleRule } = require('customize-cra'); const removeManifest = () => config => { config.plugins = config.plugins.filter( p => p.constructor.name !== "ManifestPlugin" ); return config; }; module.exports = { webpack: override( removeManifest(), fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: 'css', }), addLessLoader(), addWebpackModuleRule({ test: require.resolve('snapsvg/dist/snap.svg.js'), use: 'imports-loader?this=>window,fix=>module.exports=0', },), addWebpackAlias({ Snap: 'snapsvg/dist/snap.svg.js' }), ), devServer: overrideDevServer( ... ) }
定制 Jest 配置 – Testing
jest
配置
定制 Webpack Dev Server
通过 devServer
我们可以做一些开发环境的配置,比如设置 proxy
代理,调整 publicPath
,通过 disableHostCheck
禁用转发域名检查等。
从 CRA 2.0
开始,推荐搭配 customize-cra
使用,里面提供了一些常用的配置,可以方便我们直接使用。
const { override, overrideDevServer, } = require('customize-cra'); const addProxy = () => (configFunction) => { configFunction.proxy = { '/v2ex/': { target: 'https://www.v2ex.com', changeOrigin: true, pathRewrite: { '^/v2ex': '/' }, }, }; return configFunction; } module.exports = { webpack: override( ... ), devServer: overrideDevServer( addProxy() ) }
Paths – 路径变量
paths
里面是 create-react-app
里面的一些路径变量,包含打包目录、 dotenv
配置地址、 html
模板地址等。
module.exports = { dotenv: resolveApp('.env'), appPath: resolveApp('.'), appBuild: resolveApp('build'), appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), appIndexJs: resolveModule(resolveApp, 'src/index'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), appTsConfig: resolveApp('tsconfig.json'), appJsConfig: resolveApp('jsconfig.json'), yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveModule(resolveApp, 'src/setupTests'), proxySetup: resolveApp('src/setupProxy.js'), appNodeModules: resolveApp('node_modules'), publicUrl: getPublicUrl(resolveApp('package.json')), servedPath: getServedPath(resolveApp('package.json')), // These properties only exist before ejecting: ownPath: resolveOwn('.'), ownNodeModules: resolveOwn('node_modules'), // This is empty on npm 3 appTypeDeclarations: resolveApp('src/react-app-env.d.ts'), ownTypeDeclarations: resolveOwn('lib/react-app.d.ts'), };
比如我们要修改 appHtml
即 html
模板的默认位置,可以这样做:
const path = require('path'); module.exports = { paths: function (paths, env) { // 指向根目录的test.html paths.appHtml = path.resolve(__dirname, "test.html"); return paths; }, }
5. 常用示例
添加多页面入口
首先安装 react-app-rewire-multiple-entry
。
npm install react-app-rewire-multiple-entry --save-dev
然后在 config-overrides.js
配置:
const { override, overrideDevServer } = require('customize-cra'); const multipleEntry = require('react-app-rewire-multiple-entry')([{ entry: 'src/pages/options.tsx', template: 'public/options.html', outPath: '/options.html', }]); const addEntry = () => config => { multipleEntry.addMultiEntry(config); return config; }; const addEntryProxy = () => (configFunction) => { multipleEntry.addEntryProxy(configFunction); return configFunction; } module.exports = { webpack: override( addEntry(), ), devServer: overrideDevServer( addEntryProxy(), ) }
禁用 ManifestPlugin
const { override, } = require('customize-cra'); const removeManifest = () => config => { config.plugins = config.plugins.filter( p => p.constructor.name !== "ManifestPlugin" ); return config; }; module.exports = { webpack: override( removeManifest(), ), }
antd 按需加载 && less-loader
const { override, fixBabelImports, addLessLoader } = require('customize-cra'); module.exports = { webpack: override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: 'css', }), addLessLoader(), ), }
最后,如果使用上有什么问题欢迎留言,我会尽我所能解答大家的问题。