从create-react-app项目中挖掘精髓
前提说明
摆脱dva等框架因素, 学习React项目构建最好的方法就是跟着create-react-app项目走一遍.
- 项目地址(star数46978): create-react-app 🔗
ES6标准文档推荐:
创建项目
- 执行命令:
npx create-react-app my-app
npx是npm@5.2后自带的命令, 简化npm执行一次性命令的复杂性, 一般要用依赖node的命令行工具都要安装在全局目录下, 有了npx可以 跳过安装操作直接执行命令, 更可以直接执行github dist的脚本文件, 挺实用的.
精髓目录
- Updating to New Releases
- Sending Feedback
- Folder Structure
- Available Scripts
- npm start
- npm test
- npm run build
- npm run eject
- Supported Browsers
- Supported Language Features and Polyfills
- Syntax Highlighting in the Editor
- Displaying Lint Output in the Editor
- Debugging in the Editor
- Formatting Code Automatically
- Changing the Page
<title>
- Installing a Dependency
- Importing a Component
- Code Splitting
- Adding a Stylesheet
- Post-Processing CSS
- Adding a CSS Preprocessor (Sass, Less etc.)
- Adding Images, Fonts, and Files
- Using the
public
Folder - Using Global Variables
- Adding Bootstrap
- Adding Flow
- Adding a Router
- Adding Custom Environment Variables
- Can I Use Decorators?
- Fetching Data with AJAX Requests
- Integrating with an API Backend
- Proxying API Requests in Development
- Using HTTPS in Development
- Generating Dynamic
<meta>
Tags on the Server - Pre-Rendering into Static HTML Files
- Injecting Data from the Server into the Page
- Running Tests
- Debugging Tests
- Developing Components in Isolation
- Publishing Components to npm
- Making a Progressive Web App
- Analyzing the Bundle Size
- Deployment
- Advanced Configuration
- Troubleshooting
- Alternatives to Ejecting
- Something Missing?
Updating to New Releases
这里对项目进行了介绍, create-react-app分为两块:
- create-react-app是命令行工具
- react-scripts是创建项目的配置与执行脚本
由于做到命令行工具与执行脚本的分离, 所有不用更新create-react-app, 而它下载的react-scripts总是最新的
Sending Feedback
项目反馈
Folder Structure
新建项目后的目录结构
.
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html # 页面模板
│ └── manifest.json
├── README.md
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── registerServiceWorker.js # JavaScript代码的入口
└── yarn.lock
Available Scripts
在项目根目录可以执行的命令
npm start
: 启动开发环境npm test
: 进行代码的单元测试npm run build
: 构建与打包npm run eject
: 使用Webpack和Babel替换react-scripts用于项目启动与构建, 操作不可逆
Supported Browsers
支持的浏览器, 查看react浏览器支持情况 🔗
Supported Language Features and Polyfills
支持的语言特性及Polyfill实现, Polyfill指用现有代码实现的未来语言特性
这个项目支持最新的语言标准ES6 🔗的语法, 同时支持:
- Exponentiation Operator 🔗 (ES2016).
- Async/await 🔗 (ES2017).
- Object Rest/Spread Properties 🔗 (第三阶段提案).
- Dynamic import() 🔗 (第三阶段提案)
- Class Fields and Static Properties 🔗 (第三阶段提案一部分).
- JSX 🔗 和 Flow 🔗 语法, Flow用于变量类型的预定义.
更多对语言新特性的支持得看babel的实现, different proposal stages 🔗
部分polyfills 🔗说明, 可通过工具Babel REPL 🔗进行试验下转换:
Object.assign()
🔗, 不支持时的polyfills实现:object-assign
🔗.Promise
🔗, 不支持时的polyfills实现:promise
🔗.fetch()
🔗, 不支持时的polyfills实现:whatwg-fetch
🔗.
Syntax Highlighting in the Editor
编辑器中代码高亮, vim用户不需要
Displaying Lint Output in the Editor
编辑器中eslint语法错误检查, vim用户不需要
Debugging in the Editor
编辑器中调试代码, vim用户不需要
Formatting Code Automatically
利用Prettier自动格式化JavaScript, CSS, JSON代码
应用于在每次git的commit时自动格式化代码:
yarn add husky lint-staged prettier
- husky 🔗: git操作钩子.
- lint-staged 🔗: 用于在git的暂存文件状态时运行脚本, staged指git提交操作后的状态. 关于lint-staged的介绍 🔗.
- prettier 🔗: 用于文件的格式化.
修改文件package.json
"scripts": {
+ "precommit": "lint-staged",
"start": "react-scripts start",
"build": "react-scripts build",
"dependencies": {
// ...
},
+ "lint-staged": {
+ "src/**/*.{js,jsx,json,css}": [
+ "prettier --single-quote --write",
+ "git add"
+ ]
+ },
"scripts": {
修改后在每次git提交代码的时候都会格式化代码, 或者手动执行./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx,json,css}"
格式化指定目录, 应用于编辑器中 🔗
Changing the Page <title>
通过document.title
API修改网页标题.
Installing a Dependency
安装依赖: npm install --save react-router
或 yarn add react-router
Importing a Component
引入组件:
- ES5:
require()
和module.exports
- ES6:
import
和export
关于import引入模块的介绍可以看ES6中模块引入与导出的使用场景 🔗
Code Splitting
代码可以进行切割分块, 然后通过import()
🔗动态导入, 并返回一个Promise对象, 示例:
// moduleA.js
const moduleA = 'Hello';
export { moduleA };
// App.js
import React, { Component } from 'react';
class App extends Component {
handleClick = () => {
import('./moduleA')
.then(({ moduleA }) => {
// Use moduleA
})
.catch(err => {
// Handle failure
});
};
render() {
return (
<div>
<button onClick={this.handleClick}>Load</button>
</div>
);
}
}
export default App;
react官网示例及在react-route中使用Code-Splitting 🔗
Adding a Stylesheet
引入样式文件import './*.css';
Post-Processing CSS
如果css熟悉是浏览器专属属性, 则会自动给属性前面加浏览器前缀.
Adding a CSS Preprocessor (Sass, Less etc.)
添加css预处理器
Adding Images, Fonts, and Files
引入图片, 字体和文件import logo from './logo.png';
Using the public
Folder
当react-scripts@0.5.0
和更高版本新建项目时会在根目录创建public文件夹, 用于放置静态文件.
Webpack将不能正确解析public下的文件, 通常用%PUBLIC_URL%
及 process.env.PUBLIC_URL
返回public目录的绝对路径.
教程中推荐使用import方式引入静态文件, 相对于标签形式引入, 可以减少请求次数, 避免找不到文件报404, 避免浏览器缓存文件.
Using Global Variables
当用window存放全局变量的时候, linter会发出变量未定义的警告, 此时可以向代码后面添加// eslint-disable-line
让linter过滤当前行, 如: const $ = window.$; // eslint-disable-line
Adding Bootstrap
推荐使用蚂蚁金服出的React UI 框架 antd 🔗
Adding Flow
使用flow 🔗可以强制定义变量的类型, 通过以下方式配置:
- 安装依赖:
yarn add flow-bin
package.json
的script字段加入"flow": "flow"
- 初始化
.flowconfig
文件:yarn flow init
- 在文件头部加入
// @flow
示例: (arg: number): Array<number> => [arg, arg + 1]
, 定义传入的参数为数字类型, 并返回数组, 元素为数字类型.
Adding a Router
React没有指定路由功能用哪个模块实现, 但用的最多的还是React Router.
React Router 🔗, 点进去是个9分钟的视频, 英文的可能听不太懂, 看绝对看得懂代码, 可以看看国外开发大牛用vim写代码.
添加依赖: yarn add react-router-dom
Adding Custom Environment Variables
在项目中使用环境变量, 支持react-scripts@0.2.3
或更高版本, React中统一标准变量名前加REACT_APP_
前缀, 项目启动后环境变量放于process.env
里面.
Referencing Environment Variables in the HTML
在react-scripts@0.9.0
及更高版本, 环境变量可以在public/index.html
中使用, 如: <title>%REACT_APP_WEBSITE_NAME%</title>
Adding Temporary Environment Variables In Your Shell
在shell中定义环境变量后启动项目
Windows (cmd.exe)
set "REACT_APP_SECRET_CODE=abcdef" && npm start
Windows (Powershell)
($env:REACT_APP_SECRET_CODE = "abcdef") -and (npm start)
Linux, macOS (Bash)
REACT_APP_SECRET_CODE=abcdef npm start
Adding Development Environment Variables In .env
react-scripts@0.5.0
及更高版本支持在项目更目录下.env
文件定义环境变量, 如.env
文件中定义REACT_APP_SECRET_CODE=abcdef
, 更改完后需要重新启动项目.
What other .env
files can be used?
此特性应用于react-scripts@1.0.0
及更高版本, 定义不同环境下的env文件:
.env
: 默认.env.local
: 本地定义, 除test的所有环境.env.development
,.env.test
,.env.production
: 分别定义各个环境下的env文件.env.development.local
,.env.test.local
,.env.production.local
: 分别定义各个环境下的env本地文件
各个环境下文件读取优先级(高 -> 低):
npm start
:.env.development.local
,.env.development
,.env.local
,.env
npm run build
:.env.production.local
,.env.production
,.env.local
,.env
npm test
:.env.test.local
,.env.test
,.env
(note.env.local
无效)
项目地址: dotenv 🔗
Expanding Environment Variables In .env
.env文件的扩展, 支持react-scripts@1.1.0
及更高版本, 项目地址: dotenv-expand 🔗, 示例:
DOMAIN=www.example.com
REACT_APP_FOO=$DOMAIN/foo
REACT_APP_BAR=$DOMAIN/bar
Can I Use Decorators?
暂时不推荐使用装饰器, 但可能会在今后的稳定版本支持, 不支持原因有三:
- 装饰器语法只是个提案.
- 当前版本的Babel不支持.
- facebook内部未使用该语法.
Fetching Data with AJAX Requests
教程中推荐使用axios 🔗和fetch()
方法 🔗
发起AJAX请求, 全局变量window下的fetch方法是原生方法XMLHttpRequest的替代, 提供更利于使用的API, 请求返回Promise对象, 该Promise对象遵循
Promise/A+规范 🔗, fetch方法使用示例:
postData('http://example.com/answer', {answer: 42})
.then(data => console.log(data)) // 输出 `response.json()` 方法调用后返回的结果
.catch(error => console.error(error))
function postData(url, data) {
// Default options are marked with *
return fetch(url, {
body: JSON.stringify(data), // 必须定义请求头的'Content-Type'类型
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example', // 浏览器信息
'content-type': 'application/json' // 请求体类型
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response => response.json()) // 提取返回体中的数据解析成JSON类型数据
}
Integrating with an API Backend
与后端API集成这个用处不大, 此处省略
Proxying API Requests in Development
此配置支持react-scripts@0.2.3
或更高版本.
跨域请求(CORS)允许浏览器请求非js脚本json文件的资源, 浏览器在请求头中用Origin字段标明请求为跨域请求, 然后后端通过
Access-Control-Allow-Origin和Access-Control-Allow-Methods响应头返回验证结果, 对于js脚本的跨域请求jQuery时期用jsonp解决跨域问题,
不过在React中直接提供简单的配置实现跨域请求, 通过在package.json
文件中添加"proxy": "http://localhost:4000"
实现跨域请求,
且只能在npm start
运行的开发模式中有效.
”Invalid Host Header” Errors After Configuring Proxy
有可能命令行中报Invalid Host Header
错误, 此时可以试试在.env.development
文件中添加HOST=mypublicdevhost.com
Configuring the Proxy Manually
此特性支持react-scripts@1.0.0
及更高版本. 手动配置代理的方式相对于标准配置方法更灵活, 在package.json
文件中添加以下代码实现:
{
// ...
"proxy": {
// 匹配所有 /api 开头地址
"/api": {
"target": "<url_1>",
"ws": true
// ...
},
// 匹配所有 /foo 开头地址, 并将 /foo 改写为 /foo/beta
"/foo": {
"target": "<url_2>",
"ssl": true,
"pathRewrite": {
"^/foo": "/foo/beta"
}
// ...
},
// 会匹配 /bar/abc.html 但不匹配 /bar/sub/def.html
"/bar/[^/]*[.]html": {
"target": "<url_3>",
// ...
},
// 会匹配 /baz/abc.html 和 /baz/sub/def.html
"/baz/.*/.*[.]html": {
"target": "<url_4>"
// ...
}
}
// ...
}
Configuring a WebSocket Proxy
比如做WebRTC项目时需要代理WebSocket请求, 可以在package.json
文件中进行如下配置:
{
// ...
"proxy": {
"/socket": {
// Your compatible WebSocket server
"target": "ws://<socket_url>",
// Tell http-proxy-middleware that this is a WebSocket proxy.
// Also allows you to proxy WebSocket requests without an additional HTTP request
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
"ws": true
// ...
}
}
// ...
}
Using HTTPS in Development
此特性支持react-scripts@1.0.0
及更高版本, 在项目中使用HTTPS请求配置方式和一般代理HTTP请求一样, 不过需要定义环境变量,
定义如下开启开发环境支持HTTPS请求, 不过由于用的自签名证书, 通常每次打开网页都会有HTTPS页面不安全的警告.
Windows (cmd.exe)
set HTTPS=true&&npm start
Windows (Powershell)
($env:HTTPS = $true) -and (npm start)
(Note: the lack of whitespace is intentional.)
Linux, macOS (Bash)
HTTPS=true npm start
未完