前言

Create React App(以下简称 CRA)是创建 React 应用的一个脚手架,它与其他脚手架不同的一个地方就是将一些复杂工具(比如 webpack)的配置封装了起来,让使用者不用关心这些工具的具体配置,从而降低了工具的使用难度。

使用 create-react-app 生成项目,不会有 webpack 的配置项,要导出 webpack,必须使用 react-script eject,但这是一个单向操作,eject 后,就无法恢复了。如果只是修改一些简单的配置,eject 是没有必要的。

但是对于一些熟悉 webpack 的开发者来说,他们可能想对 webpack 配置做一些修改,这个时候应该怎么办呢?

其实我们可以通过以下几种方式来修改 webpack 的配置:

  • 项目 eject
  • 替换 react-scripts 包
  • 使用 react-app-rewired
  • scripts 包 + override 组合

如果你正在为如何处理react配置项而烦恼,以下内容可以带你走出一片蓝天。接下来对这几种方式分别进行介绍下,配置是一个痛苦的过程,认真品味,如果对你有帮助请点赞转发哦!!使用插件的方法见官方文档。

项目 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 自定义配置

注意

react-app-rewired 1.x 配合 create-react-app 1.x

react-app-rewired 2.x 配合 create-react-app 2.x

版本升级导致互不兼容,另外,react-app-rewired 2.x 应该是社区维护了。

react-app-rewired@^2.0.0+ 版本需要搭配 customize-cra 使用

在 react-app-rewired 1.x 的版本中,它除了提供覆盖配置的方法,还体用了一些 helpers,例如 rewireLess、rewirePreact 等,2.x 版本只保留了核心功能。

From README:

Version 2.0 removes the rewire helper functions

All helper functions:

  • injectBabelPlugin
  • compose
  • getBabelLoader
  • getLoader
  • babelLoaderMatcher
  • loaderNameMatches

have been removed with commit 0848602

另外一个工具帮我们实现了这些,customize-cra,这次我们将使用 react-app-rewiredcustomize-cra 一起倒腾。

虽然有这两种方式可以扩展 webpack 配置,但是很多开发者还是觉得太麻烦,有没有一种方式可以既不用eject项目又不用创建自己的 scripts 包呢?答案是肯定的,react-app-rewired 是 react 社区开源的一个修改 CRA 配置的工具。

react-app-rewired 的方式自定义配置,参考 Extended Configuration Options 文档。

这次我们使用 customize-cra 协助自定义,参考 Using the plugins 文档。

customize-cra提供了一些简遍的api(customize-cra/api.md),通常就可以满足大部分的开发需求

源码(customize-cra/src/customizes/webpack.js)(比如当前版本github.com/arackaf/cus…)

在 CRA 创建的项目中安装了react-app-rewired后,可以通过创建一个config-overrides.js 文件来对 webpack 配置进行扩展。具体操作如下:

config-overrides.js配置

1、修改webpack配置文件,需要安装 react-app-rewired customize-cra

yarn add react-app-rewired customize-cra -D复制代码

2、修改package.json文件

"scripts": {"start": "react-app-rewired start","build": "react-app-rewired build","test": "react-app-rewired test --env=jsdom","eject": "react-scripts eject"},复制代码

3、在项目根目录新建config-overrides.js

const { override } = require('customize-cra');
module.exports = {};复制代码

4、添加配置,跨域设置、增加less支持、px转rem、ant-design按需加载、打包压缩js和css

// 安装less less-loader
yarn add less less-loader -D
// 安装compression-webpack-plugin 压缩js为gzip
yarn add compression-webpack-plugin -Dconst { override, overrideDevServer, addLessLoader, addPostcssPlugins, fixBabelImports } = require('customize-cra');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
// 打包配置
const addCustomize = () => config => {if (process.env.NODE_ENV === 'production') {// 关闭sourceMapconfig.devtool = false;// 配置打包后的文件位置config.output.path = __dirname + '../dist/demo/';config.output.publicPath = './demo';// 添加js打包gzip配置config.plugins.push(new CompressionWebpackPlugin({test: /\.js$|\.css$/,threshold: 1024,}),)}return config;
}
// 跨域配置
const devServerConfig = () => config => {return {...config,// 服务开启gzipcompress: 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())
}
复制代码

5、antd

假设我们要使用 antd,参考 高级配置 文档。

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]' // 自定义 CSS Modules 的 localIdentName}),
);复制代码

6、decorators

在 create-react-app 的 Can I Use Decorators 文档中说,当前它并不是一个文档的规范,默认不推荐使用,如果要使用,需要自己手动开启。

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]' // 自定义 CSS Modules 的 localIdentName}),
);复制代码

7、添加别名

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]' // 自定义 CSS Modules 的 localIdentName}),
);复制代码

8、添加 react-hot-reloader、在根组件处开启 react-hot-reloader

# 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
复制代码

随后在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) : Appexport default AppHot
复制代码

9、关闭sourceMap

  • 方案一:修改package中scripts里的build
"build": "GENERATE_SOURCEMAP=false react-app-rewired build"复制代码
  • 方案二:
const rewiredMap = () => config => {config.devtool = config.mode === 'development' ? 'cheap-module-source-map' : falsereturn config
}复制代码

10、按需加载、lodash优化

ant-design-mobile、ant-design按需加载

亲试有如下几个方案:

方法一:
fixBabelImports('import', {libraryName: 'antd',libraryDirectory: 'es',style: true}),fixBabelImports('babel-plugin-import', {libraryName: 'antd-mobile',libraryDirectory: 'es',style: true}),
方法二:
fixBabelImports('antd', {libraryName: 'antd',libraryDirectory: 'es',style: true}),fixBabelImports('antd-mobile', {libraryName: 'antd-mobile',libraryDirectory: 'es',style: true}),复制代码

使用插件优化

lodash作为一个比较常用的前端开发工具集,在使用webpack进行vendor分离的实践中,会遇到将整个lodash文件分离到vendor.js的问题。这样会使vendor.js文件变得特别大。

根据babel-plugin-lodash参考文档介绍,使用lodash-webpack-plugin可以进一步压缩lodash

安装lodash-webpack-plugin依赖:

yarn add lodash-webpack-plugin --dev复制代码

增加配置文件如下:

const LodashWebpackPlugin = require('lodash-webpack-plugin')
addWebpackPlugin({new LodashWebpackPlugin({       collections: true,       paths: true    }),})复制代码

实际上还需要这一步支持,首先新建自己的.bable文件或者直接在package.json里面修改,下面方法采用的是前者

{"presets": ["react-app"],// 开发环境下配置项"env": {"development": {"plugins": ["dynamic-import-node"]}},"plugins": [["module-resolver",{"alias": {"src": ["./src/"]}}],["import",{"libraryName": "lodash","libraryDirectory": "","camel2DashComponentName": false}],["@babel/plugin-transform-modules-commonjs",{"allowTopLevelThis": true}]]
}复制代码

注意:一般情况下,不使用lodash-webpack-plugin就可以满足开发的需要,但是文件特别大的情况下,建议还是使用它。

使用 lodash-es

tree-shaking 作为 rollup 的一个杀手级特性,能够利用 ES6 的静态引入规范,减少包的体积,避免不必要的代码引入,webpack2 也很快引入了这个特性。

要用到 tree-shaking,必然要保证引用的模块都是 ES6 规范的。lodash-es 是着具备 ES6 模块化的版本,只需要直接引入就可以。

import {isEmpty, isObject, cloneDeep} from 'lodash-es';
复制代码

可以看到,使用优化后,lodash 从原来的 70.7kb 减少到了20.2kb效果还是很明显的。

可以参考以下链接

www.jb51.net/article/113…

www.npmjs.com/package/lod…

以上是一些简单的配置已经好了。

整体配置文件如下:

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' : falsereturn config
}
process.env.PORT = 3006process.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...')// 关闭sourceMapconfig.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: '', // prodchangeOrigin: true,secure: false,xfwd: false,pathRewrite: {'^/api': '/'}},'/store': {// target: '', // stagingchangeOrigin: 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'), // 修改 publicPathaddWebpackExternals({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-												

Create React App无eject配置(react-app-rewired 和 customize-cra)相关推荐

  1. 13、Horizon App Volumes 安装配置

    Horizon App Volumes 安装配置 Horizon App Volumes 安装配置 1.Horizon App Volumes 简介 2.Horizon App Volumes 安装 ...

  2. 微信APP支付申请配置流程

    微信APP支付申请配置流程 APP支付申请步骤 微信APP支付配置 APP支付:用户在APP内点击后跳转到微信内完成支付. APP支付申请步骤 注册开放平台账号 进入微信开放平台注册:https:// ...

  3. 一个基于 React 开发的PC端音乐App

    ?一个基于 React 开发的PC端音乐App. 同时支持 Mac 与 Windows 系统.下载地址 项目使用 electron 作为外壳,webpack 作为打包工具,核心技术包括 React + ...

  4. 一个使用react native实现的短视频APP

    黄豆仔短视频APP 一个使用react native实现的短视频APP.该项目是我没事搞着玩,用react native 写的.用了很多的库同时也修改了几个库: react-native-card-s ...

  5. Android原生App跳转到React Native App

    前言 众所周知,react native开发是前几年较火的一个话题,随着各种框架的更新换代,一些技术也会逐渐被掩埋.本篇是楼主在项目中总结出的一些开发经验.主要讲了react native和原生的ap ...

  6. react.js app_在React.JS中创建一个Weather App

    react.js app Hello readers! 各位读者好! In this article, you will know how to develop a basic weather app ...

  7. 手机app开发可选技术——React Native

    百度百科 React Native是Facebook于2015年开源的跨平台移动应用开发框架,是Facebook早先开源的JS框架React在原生移动应用平台的衍生产物,支持IOS和安卓两大平台.RN ...

  8. 什么是 Native、Web App、Hybrid、React Native和Weex?

    一句话概要 Native.Web App.Hybrid.React Native(后面以RN简称).Weex 间的异同点,后期同步小程序和PWA. App常用开发模式简介 此处App为应用,appli ...

  9. 什么是 Native、Web App、Hybrid、React Native 和 Weex?

    (点击上方公众号,可快速关注) 来源:zwwill_木羽 segmentfault.com/a/1190000011154120 一句话概要 Native.Web App.Hybrid.React N ...

  10. 前端快闪三:多环境灵活配置react

    大前端快闪:package.json文件知多少? 大前端快闪二:react开发模式 一键启动多个服务 你已经使用Create React App[1] 脚手架搭建了React应用,现在该部署了. 一般 ...

最新文章

  1. TortoiseGit配置密钥的方法
  2. 访问IIS元数据库失败解决方法(转)
  3. struts2学生信息管理系统篇章③
  4. 在Visual Studio中使用用例图描述系统与参与者间的关系
  5. linux加密框架 crypto 算法管理 - 哈希算法应用实例
  6. struts2之值栈
  7. python一定要有主函数_Python 为什么没有 main 函数?为什么我不推荐写 main 函数?...
  8. 使用fastcgi_cache加速Nginx
  9. 过来领你的Bug之“缺陷分析“篇
  10. OOP思想指的是什么?
  11. 真实!数据分析师到底是做什么的?
  12. 树莓派4B系统搭建(超详细版)
  13. matlab portcons,马科维茨投资组合理论(均方模型)学习笔记——基于Matlab(二)...
  14. 修改el-pagination分页样式
  15. 【005】基于51单片机的多路热释电红外报警器proteus仿真与实物设计
  16. 4.8 单元格背景样式的设置 [原创Excel教程]
  17. 2020-08-07#自学编程方法论# 怎么学到后面越来越难?达克效应在作祟!
  18. 跨境电商难做?你需要知道这5个“低成本低风险”跨境电商项目
  19. python操作sqlite数据库
  20. 用74ls90组成二十四进制计数器_一个厉害的芯片芯片74LS190同步计数器可以做加法也可以做减法...

热门文章

  1. Atitit q2016 q0 doc list on home ntpc.docx
  2. python实现明星专家系统:人脸识别自动比对
  3. wegame系统推荐头像_热点微信国旗头像刷屏,怎么回事?(附国旗获取方式)
  4. 育碧首款区块链游戏真的来了吗?
  5. python 实现 加减乘除,对数指数,三角反三角计算器
  6. 【AXI】解读AXI协议中的burst突发传输机制
  7. 客户信息管理系统类毕业论文文献有哪些?
  8. 游戏主机系统,破旧老电脑秒变高性能儿时复古游戏主机
  9. 凛冬已至:大厂裁员浪潮,基础福利大砍,行业饱和,大龄程序员该如何自处
  10. Android Notification使用