React提高08 Create React App

一个由Facebook官方出品的React脚手架工具,无需额外配置,迅速搭建React应用脚手架。

这里只对它进行简单的尝试和入门,如果需要进一步的学习,官网在这里文档在这里,也可以参考这篇文章进行更高阶更深入的配置和学习。

使用Create React App开发React应用不必再安装Webpack或者Babel,它们已经被内置在脚手架中了

它提供的功能:

  1. 开箱即用的React支持
  2. 开发模式和生产模式的编译
  3. 开发模式的热更新
  4. 提供了单元测试测试的接口支持
  5. 其他配置工具的默认配置(亦可以个性化配置)

快速开始

1
2
3
npx create-react-app my-app
cd my-app
npm start

React应用会运行在http://localhost:3000/,开发完成后使用npm run build打包

安装

安装需要Node的版本在8.10.0以上

1
2
3
4
5
6
7
8
# 使用npx
npx create-react-app my-app

# npm在5.1版本下不能使用npx
# 需要使用npm,首先进行全局安装
npm install -g create-react-app
# 创建应用
create-react-app my-app

运行

1
npm start

http://localhost:3000以开发模式运行React应用,页面提供了热跟新功能,在控制台会显示错误和警告。

单元测试

基本使用

1
npm test

使用Jest运行单元测试,默认情况下会运行从上次提交commit后有改动的文件的单元测试。

需要react-scripts@0.3.0及更高版本,老项目开启单元测试看这里

Jest是基于Node的运行期,速度很快,并且动过jsdom提供了浏览器的全局变量,比如window,但Jest对于DOM的测试是不准确的,它的目的是对逻辑和组件进行单元测试,而非测试DOM

在运行时Jest会自动寻找以test.js/spec.js或者__tests__文件夹下以.js结尾的文件,==这些文件可以位于src目录下任意深度的文件夹内==。

建议将测试文件(或__test__文件夹)和北侧文件放在一起,有两个好处:

  1. 便于管理,一眼就能看到文件的单元测试文件
  2. 引入组件的时候更简洁, 例如import App from './App'

命令行接口

使用npm test时,Jest会以watch模式运行,每次更改文件都会重新运行测试文件

这个模式下的命令行接口提供了各种能力,可以一直开着这个窗口进行快速的重复测试

版本管理接口

使用npm test默认情况下会运行从上次提交commit后有改动的文件的单元测试。可以在watch模式下按a来要求Jest执行全部的测试

如果当前的工程没有使用版本管理,那么Jest会默认运行全部的测试

测试组件

关于Jest的使用,以前学习在Vue中使用Jest时总结过,Jest的基本用法是相同的,在测试组件时有所区别,Vue测试组件使用的将组件和Jest进行连接的工具是Vue-test-utils,而React则是Enzyme(更准确些,jest-enzyme更接近于Vue-test-utils,封装了很多方便的API)

内容比较多,这里不展开,文档在这里,慢慢单独学习。

构建生产文件

1
npm run build

打包出的文件是经过压缩的,文件名带有Hash值

个性化配置

因为Create-React-App将Webpack、Babel、ESLit的配置隐藏起来,简化了用户的配置操作,可以快速开始开发。

但是这只适用于一些小型的、没有特殊需求的应用的开发,如果构建大型应用还需要对上面这些工具进行个性化的配置:

1
npm run eject

运行后,Create-React-App会将上面工具的配置文件复制到项目中,以后对配置文件进行修改后,项目式中会采用项目中复制修改后的配置文件

要注意,这个操作是不可逆的。

CSS-Loader

在新版本的Create React App中增加了对CSS Modules的支持,要求react-scripts版本高级2.0.0。

CSS文件的命名形式为[name].module.css,对应的类名会通过添加后缀的形式来实现局部作用域,类名的格式是[filename]\_[classname]\_\_[hash]

详情参考文档

如果需要在老版本的Create React App中增加了对CSS Modules的支持,则首先需要先通过eject命令暴露配置文件,参考这篇文章

ESLint

由于Create React App将默认的构建配置封装了起来,而ESLint仅仅开启了最基本的规则,更重要的是默认情况下,ESLint仅仅会在IDE中对违反规则的情况进行提示,并不会在构建时在终端的输出进行终端和提示。

如果这种情况可以满足需要,而只需要开启更多的规则,那么就可以在根目录下新建一个文件.eslintrc.json,然后添加:

1
2
3
{
"extends": "react-app"
}

但是如果要起到更强制性的提示作用(中断构建、终端提示),Create React App建议使用Prettier代替ESLint。如果要使用ESLint,那么就需要使用npm run eject,将配置文件吐出,按照AlloyTeam的提示进行配置即可,参考这篇笔记

Ant Design按需引入

安装antd:

1
npm install antd -S

然后进行按需引入分为两种情况:

(1)未eject出所有配置:

参考antd的文档

安装react-app-rewiredcustomize-cra(CRA)。

1
npm install react-app-rewired customize-cra -D

然后修改package.json文件的启动命令:

1
2
3
4
5
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
}

然后安装babel-plugin-import

1
npm install babel-plugin-import -D

然后在根目录下创建config-overrides.js,用来修改默认配置:

1
2
3
4
5
6
7
8
9
const { override, fixBabelImports } = require('customize-cra');

module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css', // true
}),
);

然后按按照下面的格式按需引入模块:

1
import { Button } from 'antd';

(2)已经eject出所有配置文件:

这个时候直接按照babel-plugin-import文档的说明配置即可。

安装babel-plugin-import

1
npm install babel-plugin-import -D

然后在package.json中找到babel选项,修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"babel": {
"presets": [
"react-app"
],
"plugins": [
[
"import",
{
"libraryName": "antd",
"style": "css"
}
]
]
}

引入方式与上面相同:

1
import { Button } from 'antd';

配置Less

首先安装lessless-loader

1
npm install less less-loader -D

然后同样分为是否eject配置两种情况:

(1)未eject出所有配置,仍遵循上面的步骤,安装react-app-rewiredcustomize-cra,修改package.json中的启动脚本。

然后修改config-overrides.js文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
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' },
}),
);

这里利用了less-loadermodifyVars来进行主题配置,变量和其他配置方式可以参考配置主题文档。

(2)已经eject出所有配置的情况,参考这篇文章

config目录下的webpack.config.js文件,找到// style files regexes注释位置,添加:

1
2
3
// 添加 less 解析规则
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

然后找到rules属性,在其中添加less解析配置:

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
// Less 解析配置
{
test: lessRegex,
// exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader'
),
sideEffects: true,
},
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent
},
'less-loader'
),
},

要注意的是,新添加的less-loader必须在file-loader的前面才能生效,因为Webpack在解析Loader是从右至左进行的(从下到上),只有先经过file-loader对文件路径的处理,less文件才能够被正确引入。

配置Alias

在Vue中习惯了使用@来代替一连串的..表示的相对地址,这个功能是webpack提供的,需要在webpack中进行配置

现在使用了React,也同样希望能够配置Alias来实现路径的更优雅的表示。如果已经eject处所有配置的情况下,直接在webpackd的配置文件下添加相关的代码即可:

1
2
3
4
5
6
7
8
module.exports = {
//...
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
}
}
};

但是如果在未eject的情况下,同样需要借助上面使用的react-app-rewired实现,首先在根目录建立一个alias.js文件,在这个文件中编写Alias的配置代码:react-app-rewired

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
resolve: {
alias: {
'@': path.join(__dirname, 'src'),
},
},
};

然后在config-overrides.js文件中引入并进行配置:

1
2
3
4
5
6
const { override, addWebpackAlias } = require('customize-cra');
const alias = require('./alias');

module.exports = override(
addWebpackAlias(alias.resolve.alias),
);

重新编译后@就生效了。

这时有两个问题要解决

(1)IDE的点击跳转失效了

Webstorm是可以识别webpack的配置文件,对Alias进行相应的处理,但是这里并没有eject出Webpack的配置文件,但是我们的alias.js文件就是按照Webpack的配置模块的格式来编写的,所以可以将alias.js作为配置文件,传递给Webstorm,这样IDE的文件跳转就正常了

(2)ESLint的导入导出规则报错

这是因为ESLint不能识别我们的Alias,这需要安装eslint-import-resolver-webpack这个插件,让ESLint使用Webpack的解析规则。

首先安装

1
yarn add eslint-import-resolver-webpack -D

然后在.eslintrc.js中添加如下的配置:

1
2
3
4
5
6
7
8
9
module.exports = {
"settings": {
"import/resolver": {
"webpack": {
"config": "alias.js"
}
}
},
}

同样使用alias.js来代替Webpack的配置文件,配置完之后ESLint也就能正常工作了。

配置webpack-bundle-analyzer

首先需要安装:

1
2
3
4
# NPM 
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer

在未eject的情况下,同样可以使用customize-cra来添加webpack-bundle-analyzer的配置,在cvonfig-overrides.js中,引入addBundleVisualizer,进行配置:

1
2
3
4
5
6
7
8
9
const { override, addBundleVisualizer } = require('customize-cra');

module.exports = override(
// 添加 webpack-bundle-analyzer
addBundleVisualizer({
analyzerMode: 'static',
reportFilename: 'report.html',
}, true),
);

addBundleVisualizer接受两个参数,第一个对象是webpack-bundle-analyzer的配置项,可以参考文档。第二个选项用来配置自动开启,设置为ture就不需要在每次build时传入--analyze来开启分析了

在已经eject了的情况下,在webpack.config.js做进行配置,将webpack-bundle-analyzer作为插件进行引入

1
2
3
4
5
6
7
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}

环境变量

环境变量在构建期间嵌入。

可以使用process.env.NODE_ENV来读取内置的环境变量,当运行npm start时,它等于development,当运行npm test时,它等于test,当运行npm run build时它等于production。不能手动覆盖NODE_ENV,这可以防止开发人员意外地将开发环境部署到生产环境中

除了process.env.NODE_ENV之外也可以使用自定义的环境变量,自定义的环境变量必须以REACT_APP_开头。

添加环境变量有两种方式:

(1)在Shell中添加临时环境变量。

操作系统不同,在Shell中定义环境变量的方法也不相同:

1
2
3
4
5
# Windows (cmd.exe)
set "REACT_APP_SECRET_CODE=abcdef" && npm start

# Linux, macOS (Bash)
REACT_APP_SECRET_CODE=abcdef npm start

为了统一在不同的操作系统中的设置方法,可以使用cross-env这个库

安装:

1
npm install cross-env --save-dev

使用时只需要在原来的脚本前面加上cross-env就可以了

1
cross-env NODE_ENV=development nodemon ./index.js

(2)在.env中添加开发环境变量

在项目根目录中创建名为.env的文件,在文件创建以REACT_APP_开头的自定义环境变量。除了NODE_ENV之外的任何其他变量都将被会略

==实际上,NODE_ENV是不能被覆盖的,也就意味着在.env中定义NODE_ENV也是同样被忽略的,在默认配置条件下,脚手架中的NODE_ENV是无法更改的。==

.env文件应该提交到git进行管理(除了.env&.local之外)

除了.env之外,还可以使用特殊的.env文件

  • .env:默认。
  • .env.local:本地覆盖。除test之外的所有环境都加载此文件。
  • .env.development, .env.test, .env.production:设置特定环境。
  • .env.development.local, .env.test.local,.env.production.local:设置特定环境的本地覆盖。

如果使用一个新的自定义的.env文件,比如使用.env.stage的环境变量文件,需要在运行npm命令时使用env-cmd

安装:

1
npm install env-cmd --save-dev

使用时直接将.env.stage的路径引入即可,在Package.json文件中:

1
2
3
4
5
{
"scripts": {
"test": "env-cmd ./.env.stage npm run build"
}
}

在命令行中:

1
./node_modules/.bin/env-cmd ./.env.stage npm run build

这种情况下,process.env.NODE_ENV仍然是production,但加载的.env文件已经不再是.env.build,而是变为了env.stage

参考