整理:使用create-react-app
构建的项目在yarn run eject
后的一些问题。
eject之前
create-react-app
脚手架把关于webpack
配置和其它脚本封装到了一个叫做react-scripts
的package
里。默认情况下所有的配置是不可见的,但是可以通过react-app-rewired
在不eject
的条件下修改某些webpack
配置。这样的方式在绝大多数情况下是可行的,但是如果我们想更精确的控制,比如修改默认的css打包方式、使用less
、组件库按需加载等,即使也能够做,但或多或少会用到hack
的方式。
在运行yarn run eject
后,所有关于webpack
配置将会暴露出来,在根目录会生成config和scripts两个目录,并且react-script
将不复存在,所有react-script
的依赖都将注入到项目中。
eject
是单项操作,一旦完成,就没办法还原,这就意味着复杂的webpack
管理将交由我们自己管理。
eject进行时
步骤1 运行
yarn run eject
,稍等片刻,观察到生成新增了config和scripts两个目录,并且package.json
文件被修改。打开
package.json
文件,由于我们已经不使用react-scripts
了,所以这里需要修改start build test
等命令。使用scripts
目录中对应的脚本即可,例如:1
2
3
4
5
6"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scriprs/test.js"
...
}运行
yarn start
,不出意外的话,项目正常运行,如果提示can’t find module xxx,删除node_modules
和yarn.lock
(如果有的话),重新运行yarn install
安装依赖即可。
eject后出现了问题
如果是eject
后运行项目报错,重新安装依赖后仍然报错,那么就要考虑是依赖版本的问题了。就比如笔者的这个项目,使用老版本的react-react-app
生成,并且在中期升级了react-scripts
和react
等,且使用了特定版本的eslint
规则。在根据报错提示进行修复后,运行项目仍然报错且无任何报错提示。
我们需要弄清楚到底是什么依赖有问题,这个过程是复杂且繁琐的,并且可能由于依赖前后版本已经发生了巨大的变化,即使找到存在问题的依赖也可能不能仅通过升级该依赖的版本解决问题。
所以一个备选方案是把项目整体迁移到新版本的create-react-app
上,这里有两个做法:使用create-react-app
新生成一个项目,我们把源码和依赖都迁移到新项目中,在新的项目中进行eject
;使用create-react-app
生成一个空的项目并eject
,使用新的config和scripts替换当前项目的脚本和配置,并且比较packahe.json
,更新依赖的版本,重新安装依赖,大功告成。
在实际的情况下,受制于项目版本控制、团队协作、迁移成本等,第一种方式,基本不可用。
解决完问题,接下来我们需要将写在config-overrides.js
中的自定义配置进行迁移。
自定义配置
在进行自定义配置之前,如果项目因
eslint
规则,无法运行成功,可以尝试在config/webpack.config.js
中找到eslint-loader
并且注释掉这个配置,此时在开发模式下,eslint
并不会对源码进行规则校验。
less支持
因为create-react-app
脚手架并不支持less
,我们需要手动添加对less
的支持。首先安装less-loader
,并在webpack.config.js
中的module.rules
下添加:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const lessRegex = /\.less$/;
module: {
rules: [
...
{
test: lessRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader'
),
}
]
}
注意到这里使用到了一个封装好的函数getStyleLoaders
,其针对不同的css预处理器,生成适用的loader
,源码非常简单:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
options: shouldUseRelativeAssetPaths ? { publicPath: '../../' } : {},
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
postcssNormalize(),
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
});
}
return loaders;
};
我们知道如果使用了css
预处理器,在设置loader
的时候,可能需要多个loaders
,例如:
less-loader
: 把less
编译成css
css-loader
: 解决css
使用import
、require
引入的问题style-loader
: 通过style标签把css
注入到DOM
中。- 生产环境下可能还需要生成单独的css文件,css压缩等。
而getStyleLoaders
就是生成这些loaders
的一个公共方法。
组件库按需加载
在开发过程中,如果使用到了组件库且该组件库支持按需加载(例如antd),那么我们可以根据相关的教程配置即可。
比如在当前项目中使用到了xx-ui
这个组件库,该组件库支持babel-plugin-import
按需加载,在安装好依赖后,我们只需要在package.json
的babel
下配置plugin
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15"babel": {
"presets": [
"react-app"
],
"plugins": [
[
"import",
{
"libraryName": "@wind/xx-ui",
"libraryDirectory": "es",
"style": true
}
],
]
}
这和传统的.babelrc
中进行plugin
配置无区别,但总算不用再维护额外的一个文件了。
其它
比如我们在构建时需要对代码进行拆分,我们可以在webpack.config.js
的optimization
中使用splitChunks
进行自定义、分离runtimeChunk等
。
比如我们构建时不想使用默认的static/js/[name].[contenthash:8].chunk.js
作为chunks
的文件名,直接修改即可。注意,在代码分割时,相应的css
也会被分割,如果想修改css
的配置可以直接找到MiniCssExtractPlugin
进行修改即可。
最后
经过上面所有的步骤,项目已经能够完全正常运行,和eject
之前一样。接下来我们可以详细阅读webpack.config.js
,进行更进一步的定制,也可以删除某些项目中不需要的配置,减轻webpack.config.js
的复杂性。
如果项目是老项目的话,为了项目能够在eject
后不会因为eslint
报错而无法正常运行,可以注释掉eslint-loader
来避免每次构建时候的预检,但此时对代码风格的约束只限于编辑器本身eslint
的支持,对代码质量和团队代码风格可能造成一定的影响。
更好的方式是使用一个适合这个项目的eslint
规则,如eslint-config-airbnb-base
,并且对这个第三方规则进行定制,直至这个规则适合这个项目。
完。