preface
The Create React App (CRA) is a scaffold for building React apps. The CRA is different from other scaffolds in that it encapsulates the configuration of complex tools (such as Webpack) so that users don’t have to worry about the specific configuration of these tools.
When creating projects using create-React-app, there are no configuration items for WebPack. To export WebPack, you must use React-Script eject, but this is a one-way operation, and after eject, you can’t recover. Eject is not necessary if you are modifying some simple configuration.
But for developers familiar with WebPack who might want to make some changes to their WebPack configuration, what should they do?
There are several ways to modify the WebPack configuration:
- Project eject
- Replace the react – scripts package
- Use the react – app – rewired
- Scripts package + Override combination
If you’re wondering what to do with the React configuration, here’s how to get you out of the blue. Next to introduce these ways respectively, configuration is a painful process, taste carefully, if it is helpful to you, please like forwarding oh!! See the official documentation for how to use plug-ins,
Project eject
After creating a project using CRA, the project provides this command in package.json:
{... "scripts": { "eject": "react-scripts eject" }, ... } copy codeCopy the code
After the yarn run eject command is executed, all the configurations encapsulated in the CRA are decompiled to the current project. In this way, users can gain full control of the Webpack file and modify the webpack file as desired.
The config folder will appear in the root of the project. .js │ ├── jest │ ├── csstransForm.js │ ├─ filefill.js │ ├─ paths.js │ ├─ polyfill.js ├ ─ ─ webpack. Config. Dev. Js / / development environment configuration ├ ─ ─ webpack. Config. Prod. Js / / production environment configuration └ ─ ─ webpackDevServer. Config. Copy js codeCopy the code
Another difference between CRA and other scaffolds is that CRA features can be upgraded by upgrading the React-Scripts package. For example, if you create a project with an old version of CRA, this project does not have THE PWA function, but as long as the project upgrades the version of the React-Scripts package, it can have the PWA function, and the code of the project itself does not need to make any changes.
But if you use eject, you won’t get the benefit of CRA upgrades because react-scripts already exist in your project as files, not as packages, so you can’t upgrade them.
Replace the react – scripts package
React-scripts is a core package of CRA. Default configurations for scripts and tools are integrated into the react-Scripts package. Creating projects using CRA is the default package, but CRA also provides another way to create CRA projects using custom scripts packages.
$create-react-app foo $create-react-app foo $create-react-app foo -- script-version $create-react-app foo $create-react-app foo -- script-version $create-react-app fooCopy the code
A custom package can take one of the following forms:
react-scripts
The version number of the package, for example0.8.2
This format can be used to install a lower versionreact-scripts
The package.- The name of a package that has been posted to the NPM repository, for example
your-scripts
, which contains the modified WebPack configuration. - A compressed TGZ file, for example
/your/local/scripts.tgz
, which is usually a custom scripts package not published to the NPM repository, can be usednpm pack
Command generation.
This approach is a much more flexible way to modify webPack configurations than with eject, and can be used to upgrade project features by upgrading scrips, as with CRA.
The structure of the custom scripts package can be similar to that of the React-Scripts package by modifying the corresponding WebPack configuration file and installing the required WebPack Loader or Plugin package.
Custom configuration using React-app-rewired
Pay attention to
React-app-rewired 1.x with create-react-app 1.x
React-app-rewired 2.x works with create-React-app 2.x
Version upgrades resulted in incompatibility. In addition, React-app-rewired 2.x should now be community maintained.
React-app-rewired @^2.0.0+ needs to be used with customize cra
In the React-app-Rewired 1.x version, in addition to providing the override configuration method, it also used some helpers, such as rewireLess, rewirePreact, etc. 2. X version kept only the core function.
From README:
Version 2.0 lowing the rewire helper functions
All helper functions:
- injectBabelPlugin
- compose
- getBabelLoader
- getLoader
- babelLoaderMatcher
- loaderNameMatches
have been removed with commit 0848602
Another tool does this for us, customize-cra, and this time we’re going to use React-app-rewired to reverse it together with customize-cra.
Although there are two ways to extend the WebPack configuration, many developers find it too cumbersome. Is there a way to do this without using an Eject project and without creating your own scripts package? The answer is yes, react-app-Rewired is an open-source tool for modifying CRA configurations in the React community.
To customize the Configuration purely in react-app-rewired mode, refer to the Extended Configuration Options documentation.
This time we use customize-cra to assist in customization, referring to the Using the Plugins document.
Customize-cra provides a few simplified apis (customize-cra/api.md) that typically meet most development needs
Source (customize – cra/SRC/customizes/webpack. Js), such as the current version github.com/arackaf/cus…).
After react-app-rewired is installed in a crA-created project, you can extend the WebPack configuration by creating a config-overrides. Js file. The specific operations are as follows:
The config – overrides. Js configuration
To modify webPack configuration files, install react-app-rewired customize-cra
yarn add react-app-rewired customize-cra -D
Copy the code
2. Modify the package.json file
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom",
"eject": "react-scripts eject"
},
Copy the code
3. Create config-overrides. Js in the project root directory
const { override } = require('customize-cra');
module.exports = {};
Copy the code
4. Add configuration, cross-domain Settings, add less support, PX to REM, Ant – Design load on demand, pack and compress JS and CSS
// Install less less-loader yarn add less less-loader -d // Install compression-webpack-plugin to compress js as gzip yarn add compression-webpack-plugin -D const { override, overrideDevServer, addLessLoader, addPostcssPlugins, fixBabelImports } = require('customize-cra'); const CompressionWebpackPlugin = require('compression-webpack-plugin'); // Package configuration const addCustomize = () => config => {if (process.env.node_env === 'production') {// disable sourceMap config.devtool = false; Config.output. path = __dirname + '.. /dist/demo/'; config.output.publicPath = './demo'; / / add js packaging gzip configuration config. Plugins. Push (new CompressionWebpackPlugin ({test: / \. Js $| \. CSS $/, threshold: 1024, }), ) } return config; Const devServerConfig = () => config => {return {... Gzip compress: true, proxy: {'/ API ': {target: 'XXX ', changeOrigin: true, pathRewrite: {'^/ API ': '/api', }, } } } } module.exports = { webpack: override( fixBabelImports('import', { libraryName: 'antd-mobile', style: 'css', }), addLessLoader(), addPostcssPlugins([require('postcss-pxtorem')({ rootValue: 75, propList: ['*'], minPixelValue: 2, selectorBlackList: ['am-'] })]), addCustomize(), ), devServer: overrideDevServer( devServerConfig() ) }Copy the code
5, antd
Assuming we want to use ANTD, refer to the advanced Configuration documentation.
npm i --save-dev babel-plugin-import npm i --save antd const { override, fixBabelImports, addLessLoader } = require('customize-cra'); module.exports = override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: true, }), addLessLoader({ javascriptEnabled: true, modifyVars: { '@primary-color': '#1DA57A' }, localIdentName: '[local]--[hash:base64:5]' // Customizes localIdentName of CSS Modules}),);Copy the code
6, decorators
The create-React-app Can I Use Decorators document states that it is not currently a document specification and is not recommended by default. If you want to Use it, you must manually open it yourself.
npm i --save-dev @babel/plugin-proposal-decorators const { override, fixBabelImports, addLessLoader, addDecoratorsLegacy } = require('customize-cra'); module.exports = override( addDecoratorsLegacy(), fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: true, }), addLessLoader({ javascriptEnabled: true, modifyVars: { '@primary-color': '#1DA57A'}, localIdentName: '[local]--[hash:base64:5]' // Customize CSS Modules' localIdentName}),);Copy the code
7. Add an alias
const { override, fixBabelImports, addLessLoader, addDecoratorsLegacy, addWebpackAlias } = require('customize-cra'); module.exports = override( addDecoratorsLegacy(), addWebpackAlias({ ["ag-grid-react$"]: path.resolve(__dirname, "src/shared/agGridWrapper.js") }), fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: true, }), addLessLoader({ javascriptEnabled: true, modifyVars: {'@primary-color': '#1DA57A'}, localIdentName: '[local]--[hash:base64:5]'),);Copy the code
Add react-hot-reloader and enable react-hot-reloader at the root
# https://www.npmjs.com/package/react-hot-loader
# https://github.com/cdharris/react-app-rewire-hot-loader
$ npm i react-hot-loader -D
$ npm i react-app-rewire-hot-loader @hot-loader/react-dom -D
Copy the code
Then do the following in app.js
import React, { Component } from 'react'
import { hot } from 'react-hot-loader/root'
class App extends Component {
render() {
return (
<>测试</>
)
}
}
const AppHot = process.env.NODE_ENV === 'development' ? hot(App) : App
export default AppHot
Copy the code
9. Disable sourceMap
-
Solution 1: Modify build in scripts of package
“build”: “GENERATE_SOURCEMAP=false react-app-rewired build”
-
Scheme 2:
const rewiredMap = () => config => { config.devtool = config.mode === ‘development’ ? ‘cheap-module-source-map’ : false
return config }
10. Load on demand, lodash optimization
Ant-design-mobile and ant-Design are loaded on demand
There are several plans to try:
FixBabelImports ('import', {libraryName: 'antd', libraryDirectory: 'es', style: true }), fixBabelImports('babel-plugin-import', { libraryName: 'antd-mobile', libraryDirectory: 'es', style: FixBabelImports ('antd', {libraryName: 'antd', libraryDirectory: 'es', style: true }), fixBabelImports('antd-mobile', { libraryName: 'antd-mobile', libraryDirectory: 'es', style: true }),Copy the code
Using plug-in optimization
Lodash as a common front end development tool set, in the practice of vendor separation using Webpack, will encounter the problem of separating the entire Lodash file into vvendor. This will make the vendor.js file excessively large.
According to the Babel-plugin-lodash reference documentation, you can further compress lodash using the lodash-webpack-plugin.
Install the lodash-webpack-plugin dependency:
yarn add lodash-webpack-plugin --dev
Copy the code
Add configuration files as follows:
const LodashWebpackPlugin = require('lodash-webpack-plugin')
addWebpackPlugin({
new LodashWebpackPlugin({
collections: true,
paths: true
}),
})
Copy the code
In fact, you need to support this step by either creating your own.bable file or modifying it directly in package.json
{"presets": ["react-app"], "env": {"development": {"dynamic-import-node"]}}, "plugins": [ [ "module-resolver", { "alias": { "src": ["./src/"] } } ], [ "import", { "libraryName": "lodash", "libraryDirectory": "", "camel2DashComponentName": false } ], [ "@babel/plugin-transform-modules-commonjs", { "allowTopLevelThis": true } ] ] }Copy the code
** Note: ** In general, the lodash-webpack-plugin can be used without the need for development, but it is recommended for extremely large files.
Use lodash – es
Tree-shaking, a killer feature of Rollup, can take advantage of ES6’s static introduction specification to reduce package size and avoid unnecessary code introduction, and WebPack2 quickly introduced this feature.
When using tree-shaking, it is necessary to ensure that the referenced modules are ES6 compliant. Lodash-es is a modular version of ES6 that just needs to be introduced directly.
import {isEmpty, isObject, cloneDeep} from 'lodash-es';
Copy the code
As you can see, after using the optimization, Lodash is changed from the original70.7 KBTo reduce the20.2 KBThe effect is obvious.
Check out the following links:
www.jb51.net/article/113…
www.npmjs.com/package/lod…
So that’s some simple configuration.
The overall configuration file is as follows:
const {
override,
fixBabelImports,
addLessLoader,
addWebpackAlias,
addBabelPlugins,
addWebpackPlugin,
useBabelRc,
disableChunk,
adjustWorkbox,
setWebpackPublicPath,
addBundleVisualizer,
disableEsLint,
addWebpackExternals
// addBundleVisualizer
} = require('customize-cra')
const path = require('path')
const paths = require('react-scripts/config/paths')
const rewireReactHotLoader = require('react-app-rewire-hot-loader')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
// const rewireCompressionPlugin = require('react-app-rewire-compression-plugin')
const rewireUglifyjs = require('react-app-rewire-uglifyjs')
const FilterWarningsPlugin = require('webpack-filter-warnings-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const LodashWebpackPlugin = require('lodash-webpack-plugin')// 补充:对开发友好,打包完成桌面提醒
const WebpackBuildNotifierPlugin = require('webpack-build-notifier')
const webpackConfig = require('./webpack.config.js')
// const ProgressBarPlugin = require('progress-bar-webpack-plugin')
// const Dashboard = require('webpack-dashboard')
// const DashboardPlugin = require('webpack-dashboard/plugin')
// const dashboard = new Dashboard()
const theme = require('./theme')
// SKIP_PREFLIGHT_CHECK = true
/**
* 生产环境是否打包 Source Map 两种方法
*
*/
const rewiredMap = () => config => {
config.devtool = config.mode === 'development' ? 'cheap-module-source-map' : false
return config
}
process.env.PORT = 3006
process.env.GENERATE_SOURCEMAP !== 'false'
console.log(process.env.NODE_ENV)
// const addWebpackModules = () => config => {
// const loaders = config.module.rules.find(rule => Array.isArray(rule.oneOf)).oneOf
// loaders[loaders.length - 4] = Object.assign(
// loaders[loaders.length - 4],
// webpackConfig.module.rules[0]
// )
// return config
// }
// path
const resolveAlias = dir => path.join(__dirname, '.', dir)
// 热跟新
const hotLoader = () => (config, env) => {
config = rewireReactHotLoader(config, env)
return config
}
// build--->prod --->文件设置
const appBuildPathFile = () => config => {
if (config.mode === 'development') {
console.log('evn is development, skip build path change...')
} else if (config.mode === 'production') {
console.log('evn is production, change build path...')
// 关闭sourceMap
config.devtool = false
// // 配置打包后的文件位置修改path目录
paths.appBuild = path.join(path.dirname(paths.appBuild), 'dist')
config.output.path = path.join(path.dirname(config.output.path), 'dist')
// 添加js打包gzip配置
// config.plugins.push(
// new CompressionWebpackPlugin({
// test: /\.js$|\.css$/,
// threshold: 1024
// })
// )
// 更改生产模式输出的文件名
// config.output.filename = 'static/js/[name].js?_v=[chunkhash:8]'
// config.output.chunkFilename = 'static/js/[name].chunk.js?_v=[chunkhash:8]'
}
return config
}
//生产环境去除console.* functions
const dropConsole = () => {
return config => {
if (config.optimization.minimizer) {
config.optimization.minimizer.forEach(minimizer => {
if (minimizer.constructor.name === 'TerserPlugin') {
minimizer.options.terserOptions.compress.drop_console = true
}
})
}
return config
}
}
/**
*
* @description 解决打包的时候如下报错
* @url{https://github.com/ant-design/ant-design/issues/15696}
* https://blog.csdn.net/peade/article/details/84890399
chunk 3 [mini-css-extract-plugin]
Conflicting order between:
* css ./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-7-1!./node_modules/postcss-loader/src??postcss!./node_modules/less-loader/dist/cjs.js??ref--6-oneOf-7-3!./node_modules/antd/es/input/style/index.less
* css ./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-7-1!./node_modules/postcss-loader/src??postcss!./node_modules/less-loader/dist/cjs.js??ref--6-oneOf-7-3!./node_modules/antd/es/message/style/index.less
*/
const delConflictingOrder = () => {
return config => {
for (let i = 0; i < config.plugins.length; i++) {
const p = config.plugins[i]
if (!!p.constructor && p.constructor.name === MiniCssExtractPlugin.name) {
const miniCssExtractOptions = { ...p.options, ignoreOrder: true }
config.plugins[i] = new MiniCssExtractPlugin(miniCssExtractOptions)
break
}
}
}
}
const addMiniCssExtractPlugin = () => {
return config => {
config.plugins.unshift(
new FilterWarningsPlugin({
// exclude: /any-warnings-matching-this-will-be-hidden/
// exclude: /mini-css-extract-plugin[^]*Conflicting order between:/
exclude: /\[mini-css-extract-plugin\][^]*Conflicting order between:/
})
)
}
}
const proxyApi = {
'/api': {
// target: '', // prod
changeOrigin: true,
secure: false,
xfwd: false,
pathRewrite: {
'^/api': '/'
}
},
'/store': {
// target: '', // staging
changeOrigin: true,
secure: false,
xfwd: false,
pathRewrite: {
'^/store': '/'
}
}
}
module.exports = {
webpack: override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true
}),
addLessLoader({
// strictMath: true,
noIeCompat: true,
javascriptEnabled: true,
modifyVars: { ...theme }
// localIdentName: '[local]--[hash:base64:5]', // 自定义 CSS Modules 的 localIdentName
}),
setWebpackPublicPath('/hostsec'), // 修改 publicPath
addWebpackExternals({
React: 'React',
lodash: 'Lodash'
}),
// addWebpackModules(),
addWebpackAlias({
'@': resolveAlias('src'),
lib: resolveAlias('src/lib'),
components: resolveAlias('src/components'),
images: resolveAlias('src/assets/images'),
styled: resolveAlias('src/assets/styled'),
views: resolveAlias('src/views'),
store: resolveAlias('src/store'),
router: resolveAlias('src/router'),
locale: resolveAlias('src/locale'),
// 处理警告 React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work.
'react-dom': '@hot-loader/react-dom'
// 解决antd 的icon图标打包体积大
// '@ant-design/icons': 'purched-antd-icons'
}),
disableEsLint(),
appBuildPathFile(),
disableChunk(),
dropConsole(),
// 关闭mapSource
rewiredMap(),
// 热跟新
hotLoader(),
// 配置babel解析器
addBabelPlugins(['@babel/plugin-proposal-decorators', { legacy: true }]),
//启用ES7的修改器语法(babel 7)
// ['@babel/plugin-proposal-decorators', {legacy: true}],
// ['@babel/plugin-proposal-class-properties', {loose: true}],
// 打包编译完成提醒
addWebpackPlugin(
new WebpackBuildNotifierPlugin({
title: '',
logo: path.resolve('./public/logo.svg'),
suppressSuccess: true
}),
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash].css',
chunkFilename: 'static/css/[id].[contenthash].css',
ignoreOrder: false
// moduleFilename: ({ name }) => `${name.replace('/js/', '/css/')}.css`
}),
new LodashWebpackPlugin({ collections: true, paths: true }), // 美化控制台
// new DashboardPlugin(dashboard.setData),
// 进度条
// new ProgressBarPlugin(),
delConflictingOrder(),
addMiniCssExtractPlugin()
),
rewireUglifyjs,
// rewireCompressionPlugin,
// 允许使用.babelrc文件进行Babel配置。
useBabelRc(),
// add webpack bundle visualizer if BUNDLE_VISUALIZE flag is enabled
process.env.BUNDLE_VISUALIZE == 1 && addBundleVisualizer(),
adjustWorkbox(wb =>
Object.assign(wb, {
skipWaiting: true,
exclude: (wb.exclude || []).concat('index.html')
})
)
// addDecoratorsLegacy() // 解析器,
),
// 配置devServer
// devServer: overrideDevServer(
// // dev server plugin
// watchAll(),
// ),
// 配置devServer
devServer: configFunction => (proxy, allowedHost) => {
proxy = process.env.NODE_ENV === 'development' ? proxyApi : null
// allowedHost: 添加额外的地址
const config = configFunction(proxy, allowedHost)
return config
}
}
Copy the code
㊗️ Note: If you want to add configuration, you can insert it by yourself. The specific supported methods are shown in the figure:
Portal API. * * * * md
Epilogue: Configure code parsing
At this point, some students are not satisfied with why the above code can solve the problem, I will explain according to my understanding.
const path = require('path');
Copy the code
This line of code is based on require.js, a tool for importing packages using JS. This is the line of code where you get the path, and you can do something with the path.
const paths = require('react-scripts/config/paths');
Copy the code
React-scripts contains the react project’s packaging commands, as well as the configuration files, and if you do eject, the config and script directories are strikingly similar to the directories of the same name in React-scripts. You can think of eject as exposing the configuration here. This line of code here is to get the path configuration for the project.
paths.appBuild = path.join(path.dirname(paths.appBuild), 'dist');
Copy the code
This line of code is to modify the appBuild directory in the configuration. When the React project builds, it does all the operations (such as checking the size of the code after the package, calculating Gzip, etc.) according to the configuration directory. Otherwise, the package will fail.
config.output.path = path.join(path.dirname(config.output.path), 'dist');
Copy the code
This line of code is the root of what we want to do, change the project packaging address.
conclusion
CRA is a great React scaffolding tool, but if you are not content with the Default Webpack configuration, you can extend the Webpack configuration of your project in the following ways. Each of these ways has its advantages and disadvantages, and you can choose your own way according to your specific usage scenario.
Well, that’s the end of this article. If this post has helped you, please follow, like, or bookmark it.
At the same time, welcome friends to join the wechat group to discuss:
WeChat ID
Documentation reference:
- The create – react – facebook app. Making. IO/create – fax…
- Customize – cra github.com/arackaf/cus…
- Use ant. Design /docs/react/… The basic requirements can be met, including: Introduce on demand and add Less
- Local development cross-domain add Proxy, the create – react – app website: facebook. Making. IO/create – fax…
- The HTTP proxy – middleware github.com/chimurai/ht…
- The react – hot – loader github.com/gaearon/rea…
- React-hot-loader without eject [React-app-react-hot-loader] github.com/cdharris/re…
- Create-react-app default Webpack configuration parsing and customization
- Juejin. Cn/post / 684490…