本文差点难产而死。因为总结的过程中,多次怀疑本文是对官方文档的直接翻译和简单诺列;同时官方文档很全面,全范围的介绍无疑加深了写作的心智负担。但在最终的梳理中,发现走出了一条与众不同的路,于是坚持分享出来。
希望本文除了能带领我们再次了解 Create React App(后文简称 CRA) 外,还能提供一种不同的知识组织结构和技术视角,加深我们对整个 React 技术生态的理解。
本文可能是多篇博客的综合体,整理和写作时间 15h+,仔细阅读时间 30min+,请慢用。
本文面向的读者是:
其次,本文在对官方文档进行一定的重新编排下,加上了如下创新点以完善整体的阅读学习体验:
最终,本文不涉及源码的解读,想要阅读源码的同学可以移步官方源码仓库,整体设计思路并不是很难,具体实现原理可以细细品嚼;且本文对与 CRA 不直接相关的技术点会略略而过,欢迎从点到面主动学习更多。以下是官方源码仓库以及官方文档地址:
常见的初始化 React App 的方式有:
下面我们分别进行介绍与实战练习。
React 本身专注于构建用户界面,并不依赖于某个构建工具,因此我们可以用传统的方式引入 React 并书写第一个“Hello World!” App。这种方式是快速尝试 React 的好方法,但并不适用于正式开发。
以下 HTML 代码段是一种实现方式,使用了可选的 Babel 编译和 JSX 语法,基于非构建工具的更多初始化页面的方法(如不使用 JSX 等)可以自行探索。
<html>
<head>
<script src="https://unpkg.com/react@16/umd/react.development.js">script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js">script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js">script>
head>
<body>
<div id="root">div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
)
script>
body>
html>
构建工具有很多种,目前最为主流的构建工具当属 Webpack。如何使用 Webpack 逐步构建 React App?
果不其然,为了证明 CRA 的便捷性而引出的本节 Webpack 实战,耗费了一小时多的时间进行了亲自踩坑,搜索了较多的博文都由于发布时间性而不能和最新的版本进行融合,最终根据 Github 中 react-webpack-babel 库的 package.json 文件里的相关信息才得到实现。
# 创建一个项目并进入该项目
$ mkdir react-webpack-steper & cd react-webpack-steper
# 使用默认选项直接生成一个初始化的 package.json
$ npm init -y
# 安装 React 基础包
$ sudo npm install --save react react-dom
# 安装 Webpack 相关工具 - 打包、本地启动支持、本地异步请求模拟以及热更新等
$ sudo npm install --save webpack webpack-cli webpack-dev-server
# 安装 Babel 相关工具 - 提供 ES6+ 新功能支持
$ sudo npm install --save-dev @babel/cli @babel-core @babel/preset-env @babel/preset-react
$ sudo npm install --save-dev babel-loader babel-plugin-module-resolver html-webpack-plugin
# 新建打包、编译配置文件并准备编写
$ touch webpack.config.js
$ touch .babelrc
# 新建 React 文件
$ mkdir src
$ touch src/index.js
$ touch src/index.html
其中,webpack.config.js 源码如下:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: path.resolve(__dirname, './src/index.js'),
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
publicPath: '/'
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './src/index.html')
})
]
}
.babelrc 源码如下:
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [
["module-resolver", {
"root": ["./src"]
}]
]
}
src/index.html 源码如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<div id="root">Loading...</div>
</body>
</html>
src/index.js 源码如下:
import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
)
此时,一个基于 Webpack 手动搭建的简易型“Hello World”App 开发完成,可以通过如下命令本地运行。
$ webpack-dev-server --mode development --open --hot
更多自定义内容如添加 devServer 支持、添加多页应用支持…等各种各样新技术栈的支持,也可以引申实战。
可见,不使用构建工具编写不切实际,使用构建工具手动搭建 React App 又很繁琐。因此我们需要一个能初始化一个可直接运行项目的工具,并提供各种简易的插件,Create React App 应运而生。
CRA 适用于中小型 React 项目。
CRA 将具有构建现代单页 React 应用所需的一切:
Create React App 非常适合:
如果我们想在没有数百个传递构建工具依赖的情况下尝试 React,请考虑使用单个 HTML 文件构建或使用在线沙箱平台构建。
一些支持的浏览器规则如下:
这里的重点是 BrowsersList,一个“在不同的前端工具之间共用目标浏览器和 node 版本的配置工具”。简而言之,就是 Babel 等转移工具通过我们设置的 BrowsersList 中想要支持的浏览器版本来决定哪些语法需要被编译。
一些支持的 ES 标准规则如下:
Create React App 有两个核心库,如下:
到这里,我们终于需要通过命令行来安装和使用 CRA,来构建我们的第三个“Hello World”App。
为保证每一个新项目都能使用到 CRA 最新最全的功能,请确保 CRA 为最新版本。
# 再已安装 CRA 的情况下,可以先卸载 CRA
$ npm uninstall -g create-react-app
# 正式安装 CRA
$ npm install -g create-react-app
根据我们的 npm 版本,选择相应命令来安装最新版的 CRA 并初始化第一个项目。同时检查自己的 node 版本,需要在本地开发计算机上安装 Node 8.16.0 或 Node 10.16.0 或更高版本(但服务器上不需要)。 我们可以使用nvm(macOS / Linux)或 nvm-windows 在不同项目之间切换Node版本。
# 查看自己的 npm 版本
$ npm --version
# 第一种新建项目方式——npm 5.2+ 时,以下命令会安装最新版 CRA
$ npx create-react-app my-app
# 第一种新建项目方式——npm 版本小于等于 5.1 时
$ create-react-app my-app
# 第二种新建项目方式
# npm 6+ 开始支持 npm init
$ npm init react-app my-app
# 第三种新建项目方式
$ yarn create react-app my-app
通过命令行的构建,我们初始化了第一个 CRA 项目,其中帮我们生成的项目目录结构如下(只有 src 下的文件才会被 Webpack 处理,只有 public 下的文件才能被 public/index.html 使用):
my-app
├── .git # 隐藏文件夹,会初始化第一个 Commit 记录
├── README.md
├── node_modules
├── package.json # 依赖配置文件
├── .gitignore
├── [floder_name] # 根目录下可以建立其他文件夹,但不会被用在生产环境中
├── public # 只有 public 下的文件才能被 public/index.html 使用
│ ├── favicon.ico
│ ├── index.html # public/index.html 页面模板
│ └── manifest.json
└── src # 只有 src 下的文件才会被 Webpack 处理
├── App.css
├── App.js
├── App.test.js
├── [floder_name] # 可以建立其他文件夹,以被 Webpack 成功导入
├── index.css
├── index.js # JavaScript 打包入口文件
├── logo.svg
└── serviceWorker.js
关于 package.json、index.js 和 public/index.html 文件夹,我们通过“实战 2”已经有所了解。前者是 JavaScript 打包入口文件,通常链接整个业务代码;后者是页面模板,是打包后整个静态页面的总入口。
这里对以下两个文件的出现进行简要的意义概括。
_
CRA 默认提供了运行、测试、打包、部署以及弹出项目的命令。其中的一些贴士:
# ---- 运行 ----
$ npm start
$ open http://localhost:3000
# ---- 测试 ----
$ npm test
# ---- 打包 ----
$ npm run builds
# ---- 弹出配置 ----
$ npm run eject
根据官方文档的思路,我们还能从更多角度拓展 CRA 的使用边界,下面进行概要介绍。
这里无法深入展开,每一个点都可以是一个新的实战,当我们需要某个功能时便可以查阅相关文档来主动探索。其中“分析打包文件”的解读见“实战 4”。
# 安装文件分析工具 source-map-explorer
$ sudo npm install --save source-map-explorer
# 打包项目
$ npm run build
# 将如下命令放入 package.json 中并生成快捷方式 npm run analyze
# $ source-map-explorer 'build/static/js/*.js'
# 注意此命令直接在命令行输入会提示找不到相关命令
$ npm run analyze
对于一个刚被 CRA 生成的 React App 来说,分析的结果如下,包大总计 129.38k。
回到刚才“实战 2”建立的 react-webpack-steper 项目中,当我们已经编写了一部分业务时,能否直接在当前项目中无痛引入 CRA?
解决思路便是:在大多数情况下,更改 package.json 中的 react-scripts 版本并删除不必要文依赖配置,接着在此文件夹中运行 npm install 就足够了,但最好参考更改日志以了解潜在的重大更改。CRA 致力于将重大更改保持在最低限度,以便可以轻松升级 React 脚本。
# 卸载 CRA 本身已经提供的依赖
$ sudo npm uninstall --save webpack webpack-cli webpack-dev-server
$ sudo npm uninstall --save-dev @babel/cli @babel-core @babel/preset-env @babel/preset-react
$ sudo npm uninstall --save-dev babel-loader babel-plugin-module-resolver html-webpack-plugin
# 删除 CRA 不需要使用的文件
$ rm webpack.config.js .babelrc
# 删除 node_modules
$ rm -rf node_modules
# 手动安装 React Script
$ sudo npm install --save react-scripts@latest
# 由于 CRA 默认规则,将 src/index.html 移至 public/index.html
$ mkdir public
$ mv src/index.html public
# 在 package.json 中添加 React Script 启动命令
$ vim package.json
package.json 中添加/覆盖如下指令。
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
再次执行即可。由于每个人的具体配置不一定一致,可根据自身所遇问题进行搜索。升级原理类似。
# 当没有 BrowsersList 时,CRA 会进行询问并帮助我们生成
$ npm start
CRA 官方并不推荐使用 npm run eject 弹出配置,这会增加更多的 Webpack 维护工作。对于实在想改的 Webpack 配置来说,我们可以使用 React App Rewired 库进行配置注入,这里来做个小例子。
此工具可以在不 ‘eject’ 也不创建额外 react-scripts 的情况下修改 create-react-app 内置的 webpack 配置,然后你将拥有 create-react-app 的一切特性,且可以根据你的需要去配置 webpack 的 plugins, loaders 等。
继续使用 react-webpack-steper 项目,我们的简易目标是增加 devServer 本地代理。
第一步:安装依赖并进行基础配置
# 安装依赖
$ sudo npm install --save-dev react-app-rewired customize-cra
# 根目录建立 config-overrides.js
$ touch config-overrides.js
# 修改 package.json
$ vim package.json
# 运行项目
$ npm start
其中,config-overrides.js 的初始代码为:
/* config-overrides.js */
module.exports = function override(config, env) {
//do stuff with the webpack config...
return config;
}
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"
}
第二步:编写配置,进行代理
# 新增配置文件
$ mkdir config
$ touch config/proxy.js
# 修改 config-overrides.js
$ vim config-overrides.js
其中,config/proxy.js 源码是:
module.exports = {
'/api/**': {
target: 'http://110.114.120.120:8080',
secure: false,
changeOrigin: false,
},
}
config-overrides.js 修改为:
const { overrideDevServer } = require('customize-cra')
const proxy = require('./config/proxy')
module.exports = {
devServer: overrideDevServer((config) => {
config.proxy = proxy
return config
}),
}
此时,本地的所有 api 开头的接口请求都会被转发到 http://110.114.120.120:8080 的模拟后端 IP 上。
截止目前(2020-01-10),CRA 的最新版本是 v3.3.0,我们可以从 Github 的 MileStone 中看到未来可能会改善的功能,其中整理并如下所述。
让我们一起持续关注。
回顾文章,我们从初始化 React App 的多种方式,引出 CRA 的必要性再对其进行较为充分的解释,最后配上 6 个角度来从一些角度对 CRA 的使用方式进行了实战,最后回归到 CRA 的版本展望之中。
感谢你的阅读,如果你有什么更多的疑惑,CRA 的官方文档 + 开源仓库一定会满足你的一切。
最后,一起拜读一下 CRA 和 Redux 作者、React 的核心贡献者 Dan Abramov 发布的这篇“我的十年回顾”文章。
现在我们可以开始正式深入地学习 React 技术栈了。