一、React
网址:https://reactjs.org/
1.1、概述 React 起源于 Facebook(脸书) 的内部项目,它是一个用于构建用户界面的 javascript 库,Facebook用它来架设公司的Instagram网站,并于2013年5月开源。 React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。认为它可能是将来 Web 开发的主流工具之一。 1.2、特点 ◆ 声明式 你只需要描述UI看起来是什么样式,就跟写HTML一样,React负责渲染UI ◆ 基于组件 组件时React最重要的内容,组件表示页面中的部分内容 – react元素 – 虚拟dom ◆学习一次,随处使用 使用React可以开发Web应用(React-dom),使用React可以开发移动端(react-native),可以开发VR应用(react 360) 1.3、浏览器扩展与vscode开发扩展安装 Chrome 浏览器扩展:

vscode安装react开发扩展 
1.4、第一个react应用程序 react开发需要引入多个依赖文件,其中react.js、react-dom.js这两个文件是我们创建react应用程序必须要引入的依赖文件。
react.js 是核心,提供创建元素,组件等功能 https://unpkg.com/react@17/umd/react.development.js react-dom.js 提供DOM相关功能 https://unpkg.com/react-dom@17/umd/react-dom.development.js
下载对应的react.js和react-dom.js的开发版本的js类库文件到本机中后,通过script引入到当前的网页中
// React 的核心库
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
//DOM 相关的功能
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
在html中定义reactjs渲染容器id和进行React实例化相关操作,完成helloworld显示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试一下</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script>
ReactDOM.render(React.createElement('div',{},'你好世界'),document.querySelector('#app'));
</script>
</body>
</html>


二、JSX
2.1、简介 由于通过React.createElement()方法创建的React元素有一些问题,代码比较繁琐,结构不直观,无法一眼看出描述的结构,不优雅,开发时写代码很不友好。 React使用 JSX 来替代常规的JavaScript,JSX 可以理解为的JavaScript语法扩展,它里面的标签申明要符合XML规范要求。React不一定非要使用JSX,但它有以下优点:
★ JSX 执行更快,因为它在编译为JavaScript代码后进行了优化
jsx是浏览不能够直接运行的,它需要一个转译器进行转译(babel),转为js
★ 它是类型安全的,在编译过程中就能发现错误
★ 声明式语法更加直观,与HTML结构相同,降低了学习成本,提升开发效率速
★ jsx语法中一定要有一个顶级元素包裹,否则编译报错,程序不能运行
js != jsx jsx => js+xml集合版本,js增强版,js有的它有,js没有它也有
2.2、重构helloworld 在项目中尝试 JSX 最快的方法是在页面中添加这个<script> 标签,引入解析jsx语法的babel类库,注意后续的script标签块中使用了jsx语法,则一定要申明类型 type="text/babel" ,否则babel将不进行解析jsx语法。
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
重构helloworld
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsx重构helloworld</title>
</head>
<body>
<div id="app"></div>
<script src="./js/babel.min.js"></script>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script type="text/babel">
ReactDOM.render(
<div>你好世界</div>,
document.getElementById('app');
);
</script>
</body>
</html>

2.3、在 jsx 中语法中的 js 表达式 2.3.1、嵌入JS表达式 在jsx语法中,要把JS代码写到{ }中,所有标签必须要闭合。
let num = 100
let bool = false;
var myh1 = (
<div>
{}
{num}
<hr />
{}
{true ? "条件为真" : "条件为假"}
</div>
)

2.3.2、属性绑定动态值
const title = '你好世界';
<button title={title}>按钮</button>
<style>
.btn{color:red;}
</style>
<button className="btn">按钮</button>
#jsx中绑定样式 style 只绑定对象
<div style={ {color: 'red'} }></div>
#动态显示html元素 dangerouslySetInnerHTML 可以进行对html标签进行反转义
#在react17中jsx可以直接解析html,但是对于转义后的html还不会自动转义,需要使用它
const html = 你好 ©世界
#不转义html元素输出 {两个下划线html:html}
<div dangerouslySetInnerHTML={ {__html:html} }></div>

样式的属性绑定 
2.3.3、jsx渲染数组列表
<script type="text/babel">
let arr = ["张三","李四","王五","赵六"];
let app = document.getElementById('app');
ReactDOM.render(
<div>
{}
{ arr }
</div>,
app
);
</script>
2.3.4、jsx渲染数组列表并处理
<script type="text/babel">
let nameList = ['张三', '李四', '王五'];
ReactDOM.render(
<div>
{
nameList.map((item,index) => {
return (<h3>{item}</h3>)
});
nameList.map((item,index) => <h3>{item}</h3>);
</div>,
document.getElementById('app')
);
</script>
三、React脚手架
React团队主要推荐使用create-react-app来创建React新的单页应用项目的最佳方式。 React脚手架(create-react-app)意义
? 脚手架是官方推荐,零配置,无需手动配置繁琐的工具即可使用
? 充分利用 Webpack,Babel,ESLint等工具辅助项目开发
? 关注业务,而不是工具配置
create-react-app会配置你的开发环境,以便使你能够使用最新的avaScript特性,提供良好的开发体验,并为生产环境优化你的应用程序。你需要在你的机器上安装 Node >= 8.10 和 npm >= 5.6。
建议可以安装 js包管理工具 yarn (facebook开发的) — 速度会快一些 npm i -g yarn
3.1、创建React项目
npx create-react-app my-app // 创建项目,时间会有点的久 cd my-app // 进入到安装项目中 npm start // 启动项目
 
四、React 组件

4.1、简介 组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。从概念上类似于 JavaScript 类或函数。它接受任意的入参(props),并返回用于描述页面展示内容的 React元素(jsx)。 定义好的组件一定要有返回值
react中定义组件的方式2种
? 函数组件(无状态组件/UI组件)
? 类组件(状态组件/容器组件)
4.2、组件的创建方式 4.2.1、函数创建组件
函数组件(无状态组件):使用JS的函数创建组件 函数名称必须以大写字母开头, 函数组件必须有返回值(jsx),表示该组件的结构,且内容必须有顶级元素
import React from 'react'
function Hello() {
return (
<div>这是第一个函数组件</div>
)
}

4.2.2、类组件
使用ES6语法的class创建的组件(状态组件)
类名称必须要大写字母开头
类组件要继承React.Component父类,从而可以使用父类中提供的方法或者属性
类组件必须提供 render 方法,用于页面结构渲染,结构必须要有顶级元素,且必须return去返回
import React from 'react'
class Hello extends React.Component{
render(){
return (
<div>这是第一个类组件</div>
)
}
}

4.3、父子组件传值(props 【只读属性】) 组件间传值,在React中是通过只读属性 props 来完成数据传递的。
props:接受任意的入参,并返回用于描述页面展示内容的 React元素。
props中的数据是不能被修改的,只能读取。
react中的数据不是双向,单向数据流。
4.3.1、函数组件 
4.3.2、类组件
class Cmp extends React.Component{
render(){
return (
<div>{ this.props.name } -- 我是一个类</div>
);
}
}

五、React事件处理
5.1、事件绑定 React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同: ?React 事件的命名采用小驼峰式,而不是纯小写。
onClick onChange
?使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
onClick={this.fn}
?类组件与函数组件绑定事件是差不多的,只是在类组件中绑定事件函数的时候需要用到this,代表指向当前的类的引用,在函数中不需要调用this
export default class extends React.Component {
clickHandle(e){
console.log('点了')
}
render(){
return (
<div><button onClick = {this.clickHandle}>点我点我点我</button></div>
)
}
}
类组件 
函数组件 
5.2、事件传值 ☆ 函数组件 
☆ 类组件 
5.3、事件对象 React中可以通过事件处理函数的参数获取到事件对象,它的事件对象叫做:合成事件,即兼容所有浏览器,无需担心跨浏览器兼容问题,此事件对象还拥有和浏览器原生事件相同的接口,包括 stopPropagation() 和 preventDefault() ,如果你想获取到原生事件对象,可以通过 e.nativeEvent 属性来进行获取。 作用:获取dom元素或数据传值 react中的实现没有提供像vue中的事件修饰符,像冒泡和默认行为,需要开发者自行解决。
export default class extends React.Component {
clickHandle(e){
console.log(e.nativeEvent)
}
render(){
return (
<div><button onClick = {this.clickHandle}>点我点我点我</button></div>
)
}
}
类组件 
如果使用柯里化函数绑定事件,event对象传递

5.4、this指向问题 在JSX事件函数方法中的 this,默认不会绑定 this指向。如果你忘记绑定,当你调用这方法的时候, this 的值为 undefined。所以使用时一定要绑定好this的指向。 ? 构造方法中绑定
constructor(props){
super(props)
this.fun = this.fun.bind(this)
}
? 申明是使用bind绑定
<button onClick={this.fun.bind(this)}>按钮</button>
? 箭头函数绑定
<button onClick={(evt) => this.fun(evt)}>按钮</button>
? 定义事件方法使用箭头函数来绑定
fn = (evt) => {
alert(123);
}

六、State状态
state状态只在class类组件才有,函数组件没有此功能。 6.1、基本使用 ◆ 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用 ◆ state的值是对象,表示一个组件中可以有多个数据 ◆ 通过this.state来获取状态,react中没有做数据代理 ◆ state数据值可以修改 this.setState ◆ state可以定义在类的构造方法中也可以写在类的成员属性中
export default class extends React.Component {
constructor(props){
super(props)
this.state = {
count : 0
}
}
render(){
return (
<div>计数器 :{this.state.count}</div>
)
}
}
6.2、修改状态 state中的值不能直接通过修改state中的值来进行修改数据操作,react提供一个this.setState 方法来完成state数据的修改操作
setState() 作用:
1.修改 state
2.更新UI
注:setState本身并不是异步,只是因为react的性能优化机制体现为异步。在react的生命周期函数或者作用域下为异步,在原生的环境下为同步。
总结:setState即是异步也是同步
语法1
this.setState({
key:value
})
语法2 推荐
this.setState(state => {
return {key:value}
})
##异步
state = {
number:1
};
componentDidMount(){
this.setState({number:3},()=>{
console.log(this.state.number)
})
}
##同步
state = {
number:1
};
componentDidMount(){
setTimeout(()=>{
this.setState({number:3})
console.log(this.state.number)
},0)
}
state = {
number:1
};
componentDidMount() {
document.body.addEventListener('click', this.changeVal, false);
}
changeVal = () => {
this.setState({
number: 3
})
console.log(this.state.number)
}
  
6.3、props与state区别 ? props 中存储的数据,都是外界传递到组件中的 ? props 中的数据,都是只读的 ? state 中的数据,都是可读可写的 ? props 在函数声明或类申明的组件中都有 ? state 只有类申明的组件中才有
七、props进阶
7.1、children属性 children属性,表示组件标签的子节点,当组件标签有子节点时,props就会有该属性,与与普通的props一样,其值可以使任意类型。单标签和双标签中没有数据都是没有此属性。
class App extends React.Component {
render() {
return (
<div>
<Cmp>我是children中的值</Cmp>
</div>
)
}
}
{props.children}

7.2、类型限制(prop-types) 对于组件来说,props是外部传入的,无法保证组件使用者传入什么格式的数据,简单来说就是组件调用者可能不知道组件封装着需要什么样的数据,如果传入的数据不对,可能会导致程序异常,所以必须要对于props传入的数据类型进行校验。
? 安装校验包 npm i -S prop-types
# 在组件中导入
import PropTypes from 'prop-types'
# 函数组件
function App(){}
App.propTypes = {
prop-name:PropTypes.string
}
# 类组件
class App extends Component{
static propTypes = {
prop-name:PropTypes.string
}
}
? 约束类型
https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html#proptypes
- 类型: array、bool、func、number、object、string
- React元素类型:element
- 必填项:isRequired·‘’’
- 特定结构的对象: shape({})
? 类组件 
? 函数组件 
7.3、默认值(defaultProps) 如果props没有属性没有传过数据,为了不让程序异常,可以设置其默认值。
# 函数组件
function App(){}
App.defaultProps = {
title: '标题'
}
# 类组件
class App extends Component {
static defaultProps = {
title: '标题'
}
}
函数组件默认值 
类组件默认值设置 
八、表单处理
8.1、受控组件 将state与表单项中的value值绑定在一起,有state的值来控制表单元素的值,称为受控组件。 绑定步骤: ? 在state中添加一个状态,作为表单元素的value值 ? 给表单元素绑定change事件,将表单元素的值设置为state的值
<input type="text" value={this.state.username} onChange={this.inputChange.bind(this)} />
this.setState({
[e.target.name]: e.target.value
})
8.2、非受控组件 没有和state数据源进行关联的表单项,而是借助ref,使用元素DOM方式获取表单元素值 使用步骤 ? 调用 React.createRef() 方法创建ref对象 ? 将创建好的 ref 对象添加到文本框中 ? 通过ref对象获取到文本框的值
class App extends React.Component {
constructor(props){
super(props)
this.username = React.createRef()
}
fn =() => {
console.log(this.username.current.value)
}
render(){
return (
<div>
<input type ="text" ref={this.username} />
<button onClick ={this.fn}>获取值</button>
</div>
)
}
}
九、生命周期
函数组件无生命周期,生命周期只有类组件才拥有。 生命周期函数指在某一时刻组件会自动调用并执行的函数。React每个类组件都包含生命周期方法,以便于在运行过程中特定的阶段执行这些方法。例如:我们希望在第一次将其呈现到DOM时设置一个计时器Clock。这在React中称为“安装”。我们也想在删除由产生的DOM时清除该计时器Clock。这在React中称为“卸载”。 参考:链接 完整的生命周期图 
常用的生命周期图 
? constructor(props) React组件的构造函数在挂载之前被调用。在实现React.Component构造函数时,需要先在添加其它内容前,调用super(props),用来将父组件传来的props绑定到继承类中。 只调用一次
constructor(props) {
super(props)
}

? static getDerivedStateFromProps(nextProps, prevState) 此方法是react16.3之后新增,会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。 此方法适用于罕见的用例,即当前组件的 state 的值在任何时候都取决于 props传入。 state = {
num: 0
};
render() {
return <div>当前的num是{this.state.num}</div>;
}
static getDerivedStateFromProps(nextProps, prevState) {
return null
}
? render() render()方法是必需的,它主要负责组件的渲染,会被重复调用若干次 ?componentDidMount 它会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。 ? shouldComponentUpdate(nextProps, nextState) 当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true则组件继续渲染,为false则当前组件不会渲染。首次渲染或使用 forceUpdate() 时不会调用该方法。此方法仅作为性能优化的方式而存在。你也可以考虑使用内置的 PureComponent 组件,而不是手动编写 shouldComponentUpdate()。PureComponent 会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。 当this.setState()修改了state中的数据后,当前组件将重新渲染,同时也会重新渲染子组件,但只会渲染当前组件子树(当前组件以其所有子组件) 
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.b === this.props.b) {
return false;
} else {
return true;
}
}
import React, { PureComponent } from "react";
class Cmp extends PureComponent {
render() {
console.log("Cmp被渲染了");
return <div>父亲传递过来的值b为:{this.props.b}</div>;
}
}
export default Cmp
? getSnapshotBeforeUpdate(prevProps, prevState) 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息,此生命周期的任何返回值将作为参数传递给 componentDidUpdate() ? componentDidUpdate(prevProps, prevState, snapshot) 会在数据更新后会被立即调用。首次渲染不会执行此方法。 ? componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作 ? componentWillMount() / UNSAFE_componentWillMount() 已被弃用,在挂载之前被调用。它在 render() 之前调用,因此在此方法中同步调用 setState() 不会触发额外渲染 ? componentWillReceiveProps()/UNSAFE_componentWillReceiveProps(nextProps) 已被弃用,它会在已挂载的组件接收新的 props 之前被调用。 ? componentWillUpdate()/UNSAFE_componentWillUpdate(nextProps, nextState) 已被弃用,当组件收到新的 props 或 state 时,会在渲染之前调用此方法,初始渲染不会调用此方法。
十、组件传值
10.1、父组件与子组件传值 ? 父组件将自己的状态传递给子组件,子组件当做属性来接收,当父组件更改自己状态的时候,子组件接收到的属性就会发生改变 props ? 父组件利用ref对子组件做标记,通过调用子组件的方法以更改子组件的状态,也可以调用子组件的方法(说明:ref获取当前组件对象,只针对的是使用类创建的组件才可以用此方案,类有实例,而函数没有实例)
注:ref只能给类组件去使用,得到的是组件实例,而函数组件没有实例的
 ref的另类写法 
10.2、子组件传值给父组件 父组件将自己的某个方法传递给子组件,在方法里可以做任意操作,比如可以更改状态,子组件通过this.props接收到父组件的方法后调用,来给父组件传值。
因为在react中props是可以传任意类型数据,而函数是不是也是任意类型数据之一
父组件 props传过来当前自身的函数体 子组件通过props得到 this.props.名称()执行

10.3、跨组件通信 在react没有类似vue中的事件总线来解决这个问题。在实际的项目中,当需要组件间跨级访问信息时,如果还使用组件层层传递props,此时代码显得不那么优雅,甚至有些冗余。在react中,我们还可以使用context来实现跨级父子组件间的通信。 Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据。 在React的Context中,数据我们可当成商品,发布数据的组件会用provider身份(卖方),接收数据的组件使用consumer身份(卖方) 
? 创建Context对象 当React渲染一个订阅了这个Context对象的组件,这个组件会从组件树中离自身最近的那个匹配的Provider中读取到当前的context值。
import { createContext } from "react"
export default createContext()

? 发布消息 在App.jsx组件中发布消息,这样所有的组件都可以消费它的消息。
import context from "./Context ";
let { Provider } = context;
class App extends Component {
state = {
count: 12345,
};
render() {
return (
<div>
<Provider value={this.state.count}>
<Cmp1></Cmp1>
<Cmp2></Cmp2>
</Provider>
</div>
);
}
}
export default App;

? 组件消费 在子组件中通过Api完成消费动作,从而实现消息通信。消费的方式有2种: 方式1:通过组件消费
import context from "../Context ";
let { Consumer } = context;
class Cmp1 extends Component {
render() {
return (
<div>
<Consumer>
{(value) => {
return <div>获取到的值是:{value}</div>;
}}
</Consumer>
</div>
);
}
}
export default Cmp1;
方式2:通过绑定静成属性来消费
import React, { Component } from "react";
import context from "../Context ";
class Cmp2 extends Component {
static contextType = context;
render() {
return <div>{this.context}</div>;
}
}
export default Cmp2;
十一、网络请求
11.1、axios
react中通过npm来安装axios扩展 npm i -S axios
11.2、发起请求 以请求接口地址https://api.i-lynn.cn/ip为例,请求完毕后将当前我们自己的IP地址显示在视图中。
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
ipInfo: {},
};
render() {
let { ip, country, area } = this.state.ipInfo;
return (
<div>
当前的IP地址是:{ip} - {country} / {area}
</div>
);
}
async componentDidMount() {
let ret = await axios.get("https://api.i-lynn.cn/ip");
this.setState(() => {
return {
ipInfo: ret.data,
};
});
}
}
export default App;
  列表展示 
11.3、react的反向代理 在配置在src/setupProxy.js文件,并通过npm安装http-proxy-middleware,代理中间件模块 npm i -S http-proxy-middleware 配置反向代理
const {createProxyMiddleware: proxy} = require('http-proxy-middleware')
module.exports = app => {
app.use('/api', proxy({
target: 'http://localhost',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}))
}

十二、高阶组件(HOC)
高阶函数: https://blog.csdn.net/weixin_46797477/article/details/108810821
? 把函数当作参数传递给另一个函数
? 把函数作为另一个函数的返回结果
高阶组件(Higher-Order Components)就是一个函数,传给它一个组件,它返回一个新的组件。 高阶组件:就相当于手机壳,通过包装组件,增强组件功能。
实现步骤: ? 首先创建一个函数 ? 指定函数参数,参数应该以大写字母开头 ? 在函数内部创建一个类组件或函数组件,提供复用的状态逻辑代码,并返回 ? 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件(可选,如有) ? 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面
作用: ? 进行权限控制 ? 路由限制 ? 访问统计 ? 统一布局
12.1、传统函数方式调用
# 定义高阶组件
function HocComp(Cmp) {
class Hoc extends React.Component {
state = {
a: 0,
b: 0
}
render() {
return <Cmp {...this.state} />
}
}
return Hoc
}
# 使用高阶组件
function Cmp(props) {
return (
<p>
a的值:{props.a}
<hr/>
b的值:{props.b}
</p>
)
}
let Mycmp = HocComp(Cmp)
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
高阶组件
<Mycmp></Mycmp>
</div>
)
}
}
定义和使用高阶组件 
对于数据的处理

12.2、使用装饰器调用 装饰器是一种函数,写成 @函数名 。它可以放在类和类方法的定义前面。react脚手架创建的项目默认是不支持装饰器,需要手动安装相关模块和添加配置文件。
? 安装相关模块
npm i -D customize-cra react-app-rewired
? 修改package.json文件中scripts命令
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}

? 在项目根目录中添加config-overrides.js配置文件
此文件可以理解为就是webpack.config.js的扩展文件
const path = require('path')
const {addDecoratorsLegacy, override} = require('customize-cra')
const customize = () => (config) => {
config.resolve.alias['@'] = path.join(__dirname, 'src')
return config
}
module.exports = override(
addDecoratorsLegacy(),
customize()
)

12.3、memoization(计算属性-记忆组件) 连续两次相同传参,第二次会直接返回上次的结果,每次传参不一样,就直接调用函数返回新的结果,会丢失之前的记录,并不是完全记忆,可以在它的参数中传入state数据从而实现了类似Vue中的计算属性功能
# 安装
npm i -S memoize-one
# 引入
import memoizeOne from 'memoize-one'
# 使用
getValue = memoizeOne((x,y) => x+y)
# 调用
render(){
let total = this.getValue(this.state.x, this.state.y)
return <div>{total}</div>
}

十三、css-in-js
13.1、介绍 CSS-in-JS是一种技术,而不是一个具体的库实现。简单来说CSS-in-JS就是将应用的CSS样式写在JavaScript文件里面,而不是独立为一些css,scss或less之类的文件,这样你就可以在CSS中使用一些属于JS的诸如模块声明,变量定义,函数调用和条件判断等语言特性来提供灵活的可扩展的样式定义。CSS-in-JS在React社区的热度是最高的,这是因为React本身不会管用户怎么去为组件定义样式的问题,而Vue有属于框架自己的一套定义样式的方案。 styled-components 应该是CSS-in-JS最热门的一个库,通过styled-components,你可以使用ES6的标签模板字符串语法,为需要styled的Component定义一系列CSS属性,当该组件的JS代码被解析执行的时候,styled-components会动态生成一个CSS选择器,并把对应的CSS样式通过style标签的形式插入到head标签里面。动态生成的CSS选择器会有一小段哈希值来保证全局唯一性来避免样式发生冲突。 13.2、安装 npm i -S styled-components 13.3、定义样式
# 样式js文件
import styled from 'styled-components'
const Title = styled.div`
color:red;
font-size:16px;
h3{
color:blue;
font-size:20px;
}
`
export {
Title
}
# 显示
import React, { Component } from 'react'
import { Title } from './Styles'
class App extends Component {
render() {
return (
<div>
<Title>
我只是一个标题
<h3>你好世界</h3>
</Title>
</div >
);
}
}
export default App

13.4、样式继承
# 样式
import styled from 'styled-components'
const Button = styled.button`
font-size: 20px;
border: 1px solid red;
border-radius: 3px;
`;
const Button2 = styled(Button)`
color: blue;
border-color: yellow;
`;
export {
Button,
Button2
}
# 显示
import React, { Component } from 'react'
import {
Button,
Button2
} from './Styles'
class App extends Component {
render() {
return (
<div>
<Button>我是一个按钮1</Button>
<Button2>我是一个按钮2</Button2>
</div >
);
}
}
export default App

13.5、属性传递
# 样式
import styled from 'styled-components'
const Input = styled.input`
color: ${props => props.inputColor || "blue"};
border-radius: 3px;
`;
export {
Input
}
# 显示
import React, { Component } from 'react'
import {
Input
} from './Styles'
class App extends Component {
render() {
return (
<div>
<Input defaultValue="你好" inputColor="red"></Input>
</div >
);
}
}
export default App

十四、状态管理(redux)
14.1、简介 https://www.redux.org.cn/ 2013年 Facebook 提出了 Flux 架构的思想,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。 简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。如果你的项目组件的数量和层级也变得越来越多,越来越深,此时组件间的数据通信就变得异常的复杂和低效,为了解决这个问题,引入了状态管理(redux)从而很好的解决多组件之间的通信问题。 
14.2、安装Redux
redux不是内嵌在react框架中,使用时需要手动去安装 npm i -S redux
14.3、三大原则
? 单一数据源
整个应用的 state 被储存在一棵对象结构中,并且这个对象结构只存在于唯一一个 store 中
? State 是只读的
redux中的state只读的不可以直接修改
? 使用纯函数(reducer)来执行修改state
为了修改了state数据,redux定义了一个reducer函数来完成state数据的修改,reducer会接收先前的 state 和 action,并返回 新的 state
操作原理图: 
①、store通过reducer创建了初始状态 ②、component通过store.getState()获取到了store中保存的state挂载在了自己的状态上 ③、用户产生了操作,调用了actions 的方法 ④、actions的方法被调用,创建了带有标示性信息的action(描述对象) ⑤、actions将action通过调用store.dispatch方法发送到了reducer中 ⑥、reducer接收到action并根据标识信息判断之后返回了新的state ⑦、store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行,此时就可以通知component去重新获取state
14.4、使用redux 计数器案例: ? 创建统一的数据源 在src目录下新建store/index.js作为数据源声明文件
import { createStore } from "redux"
const defaultState = {
count: 0
}
function reducers(state = defaultState, action) {
return state
}
const store = createStore(reducers)
export default store

? 组件中获取/设置数据
import React, { Component } from "react"
import store from './store'
class App extends Component {
constructor(props){
super(props)
this.state = store.getState()
store.subscribe(() => {
this.setState(state => store.getState())
});
}
render() {
return (
<div>
{ }
</div>
);
}
incr() {
const actionCreator = {
type: 'incr',
payload: 1
}
store.dispatch(actionCreator);
}
}
export default App;

? 安装redux-devtools 为了方便调试redux(可选安装),建议去谷歌商店安装redux-devtools 
在使用的时候需要参考其说明页面,参考其使用说明给代码做对应的调整。
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
随后打开浏览器调试工具就可以看到类似如下界面: 
14.5、代码模块化 现在我们已经能够很好的进行redux的数据管理,但是有一个缺点就是所有的代码都写在一个文件中,需要按照模块化开发的规则进行对代码拆分。
store
|- reducer目录
|- methods目录
|- actions目录
|- index.js入口文件

十五、react-redux
网址:https://react-redux.js.org/ React-Redux是Redux的官方针对React开发的扩展库,默认没有在React项目中安装,需要手动来安装。react-redux是依赖于redux,所以你必须安装redux 你可以理解为react-redux就是redux给我们提供一些高阶组件,能解决的问题是:使用它以后我们不需要在每个组件中再去手动订阅数据的更新了。
npm i -S react-redux redux
? 定义Provider 在程序主文件index.js文件中,定义Provider 此处类似于之前跨组件通信处的Provider一样,旨在让全局的组件共享store中的数据
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)

? 在子组件中使用react-redux
import { connect } from 'react-redux'
class Cmp extends Component {}
const mapStateToProps = state => ({
state
});
const mapDispatchToProps = dispatch => ({})
export default connect(
mapStateToProps,
mapDispatchToProps
)(Cmp)
  在组件中去使用react-redux的方案 
十六、redux中间件
16.1、介绍 redux默认支持同步操作,异步操作怎么办?Action发出以后,Reducer立即算出 State,这叫做同步;Action 发出以后,过一段时间再执行 Reducer,这就是异步。怎么才能Reducer 在异步操作结束后自动执行呢?这就要用到新的工具:中间件。中间件有很多,这里使用一个 Redux 官方出品的 中间件库:redux-thunk 
16.2、使用redux-thunk 它支持函数返回,本质还是在在内部调用dispatch返回一个固定值对象
npm i -S redux-thunk
在createStore实例store中使用
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
const store = createStore(
reducer,
applyMiddleware(thunk)
)
export default store
const composeEnhancers = (typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
const store = createStore(
reducer,
composeEnhancers(applyMiddleware(thunk))
);
# action
export const incrCount = () => dispatch => {
setTimeout(() => {
dispatch({
type: 'add',
payload: 1
})
}, 1000)
}
十七、Redux模块化
Redux 提供了一个combineReducers方法,用于 Reducer 的拆分 。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。
import { combineReducers } from 'redux'
import admin from './reducer/admin'
import web from './reducer/web'
const reducer = combineReducers({
admin,
web
})
在redux入口文件中引入combineReducers方法,进行对于多个reducer合并操作
注:reducer中的action.type名称不要有重复,如要有重复它会执行N次

十八、路由
18.1、介绍 现代的前端应用大多数是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器压力更小,所以更受欢迎。为了有效的使用单个页面来管理多页面的功能,前端路由应运而生。
? 前端路由功能:让用户从一个视图(组件)导航到另一个视图(组件)
? 前端路由是一套映射规则,在React中,是URL路径与组件的对应关系
? 使用React路由简单来说,就是配置路径和组件

18.2、路由使用 https://reactrouter.com/ 18.2.1、安装路由模块
路由模块不是react自带模块,需要安装第3方模块 npm i -S react-router-dom
18.2.2、相关组件
? Router组件:包裹整个应用,一个React应用只需要使用一次
Router: HashRouter和BrowserRouter
HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
BrowserRouter:使用H5的history API实现(localhost3000/first)
? Link/NavLink组件:用于指定导航链接(a标签)
最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
? Route组件:指定路由展示组件相关信息(组件渲染)
path属性:路由规则,这里需要跟Link组件里面to属性的值一致
component属性:展示的组件
各组件关系示意图

定义项目使用路由,在入口文件/src/index.js文件中定义路由模式 
定义路由规则和匹配成功的渲染组件 
在浏览器中输入后尝试匹配 
18.3、声明式导航 使用Link或NavLink组件完成声明式导航的定义
Link/NavLink区别
? Link组件不会根据路由的变化而添加或修改编译后html标签中的属性
? NavLink会根据路由的变化而自动修改编译后html标签中的属性
如果当前的路由规则和Navlink中的To所写的规则一致则添加class样式,
默认名称为 active,可以通过activeClassName来修改匹配成功后样式名称。
import React, { Component } from 'react'
import { HashRouter as Router, Route, Link } from 'react-router-dom'
import Home from './pages/Home'
import News from './pages/News'
class App extends Component {
render() {
return (
<Router>
<h3>导航区域</h3>
<hr />
<ul>
<li>
<Link to="/home">首页</Link>
</li>
<li>
<NavLink to="/news">新闻</NavLink>
</li>
</ul>
<hr />
<Route path="/home" component={Home} />
<Route path="/news" component={News} />
</Router>
);
}
}
export default App

18.4、编程式导航 react-router-dom中通过history对象中的push/replace/go等方法实现编程式导航功能。
this.props.history.push({
pathname: "/home",
search: "from=404",
state: {
username: "admin",
},
});
注:在react路由中this.props要想得到路由中的对象,则默认必须要通过路由规则匹配渲染的组件才能有此对象 - 必须是直接渲染的组件

18.5、路由参数 路由参数:在Route定义渲染组件时给定动态绑定的参数。
React路由传参方式有三种:
? 动态路由参数(param)
以“/detail/:id”形式传递的数据
在落地组件中通过this.props.match.params得到
? 查询字符串(query)
通过地址栏中的 ?key=value&key=value传递
在落地组件中通过this.props.location.search得到
? 隐式传参(state),通过地址栏是观察不到的
通过路由对象中的state属性进行数据传递
在落地组件中通过this.props.location.state得到

404页面定义 
18.6、嵌套路由 在有一些功能中,往往请求地址的前缀是相同的,不同的只是后面一部份,此时就可以使用多级路由(路由嵌套)来实现此路由的定义实现。
例如,路由规则如下
admin/index
admin/user
它们路由前缀的admin是相同的,不同的只是后面一部份。 实现方式: ? 先需要定义个组件,用于负责匹配同一前缀的路由,将匹配到的路由指向到具体的模块
<Route path="/admin" component={Admin}></Route>
? 创建模块路由组件负责指定各个路由的去向
render() {
let prefix = this.props.match.path;
return (
<div>
<h1>欢迎使用后台管理程序</h1>
<Route path={`${prefix}/user`} component={User}></Route>
<Route path={`${prefix}/goods`} component={Goods}></Route>
</div>
);
}

18.7、三种路由渲染方式 18.7.1、component (组件对象或函数)
<Route path="/home" component={Home} />
<Route path="/home" component={(router)=><Home {…router} />} />
18.7.2、render (函数)
<Route path="/home" render={router=><Home {…router} />} />


18.7.3、children (函数或组件)
<Route path="/about" children={router =>{
return <div>children渲染</div>
}} />
或
<Route path="/about" children={<About />} />

18.8、withRouter高阶组件 作用:把不是通过路由直接渲染出来的组件,将react-router 的 history、location、match 三个对象传入props对象上 默认情况下必须是经过路由匹配渲染的组件才存在this.props,才拥有路由参数,才能使用编程式导航的写法,执行this.props.history.push(’/uri’)跳转到对应路由的页面,然而不是所有组件都直接与路由相连的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,此时就可以使用this.props
import { withRouter} from 'react-router-dom'
export default withRouter(Cmp)
 18.9、自定义导航组件 为何需要自定义导航? 因为在项目中往往不是所有的声明式导航都是需要a标签完成,有时候可能需要别的标签,此时如果在需要的地方去写编程式导航就会有代码重复可能性,就在对于公共代码进行提取。
思路:
? 定义一个普通组件可以是类组件也可以是函数式组件
? 父组件能向子组件传值 props
? 此高阶组件不管路由规则是否匹配都要有渲染 children
 
十九、过渡动画组件
https://reactcommunity.org/react-transition-group/css-transition 19.1、基础使用 在项目中可能会有一些动画效果展示或是页面切换效果,css动画的方式,比较局限,涉及到一些js动画的时候没法处理了。react-transition-group是react的第三方模块,借住这个模块可以更方便的实现更加复杂的动画效果 ? 安装
npm i -S react-transition-group
? 使用
state = {
show: true
}
handleToggle = () => {
this.setState({
show: !this.state.show
})
}
render(){
return (
<CSSTransition
in={this.state.show}
timeout={1000}
classNames: '样式前缀名称'
unmountOnExit
>
<div>玩转React Transition</div>
</CSSTransition>
<button onClick={this.handleToggle}>Animation</button>
)
}
.wuchen-enter {
opacity: 0;
}
.wuchen-enter-active {
opacity: 1;
transition: opacity 200ms;
}
.wuchen-exit {
opacity: 1;
}
.wuchen-exit-active {
opacity: 0;
transition: opacity 200ms;
}
19.2、结合animate.css animate.css动画库集成到react-transation-group动画模块中 网址:https://animate.style/ 通过cdn地址下载动画库 
放到项目中的src/assets目录中 集成到CssTransation组件中
<CSSTransition
in={this.state.show}
timeout={1000}
classNames={{
enter: 'animate__animated',
enterActive: 'animate__fadeIn',
exit: 'animate__animated',
exitActive: 'animate__fadeOut'
}}
unmountOnExit
>
<div>玩转React Transition</div>
</CSSTransition>

19.3、列表过渡
<TransitionGroup>
<CSSTransition
key={变量}
timeout={1000}
classNames={{
enter: 'animate__animated',
enterActive: 'animate__fadeIn',
exit: 'animate__animated',
exitActive: 'animate__fadeOut'
}}
unmountOnExit
>
<div>玩转React Transition</div>
</CSSTransition>
<CSSTransition
key={变量}
timeout={1000}
classNames={{
enter: 'animate__animated',
enterActive: 'animate__fadeIn',
exit: 'animate__animated',
exitActive: 'animate__fadeOut'
}}
unmountOnExit
>
<div>玩转React Transition</div>
</CSSTransition>
</TransitionGroup>

19.4、路由过渡 路由切换时添加动画效果
render() {
return (
<div>
<li><Link to="/home">Home</Link></li>
<li><Link to="/child">Child</Link></li>
<TransitionGroup>
<CSSTransition
timeout={1000}
classNames='fade'
unmountOnExit
key={this.props.location.key}
>
<Switch>
<Route path="/home" component={Home} />
<Route path="/child" component={Child} />
</Switch>
</CSSTransition>
</TransitionGroup>
</div>
)
}

19.5、利用高阶组件给组件添加动画 并不想让所有的路由都有动画效果,只是想对指定的页面有路由切换效果,可以利用高阶组件来完成。让组件有动画
# 定义高阶组件
import React, { Component } from 'react'
import { CSSTransition } from 'react-transition-group'
const withAnimation = Cmp => {
return class extends Component {
render() {
return (
<CSSTransition
in={this.props.match !== null}
timeout={600}
classNames={{
enter: 'animate__animated',
enterActive: 'animate__fadeIn',
exit: 'animate__animated',
exitActive: 'animate__fadeOut'
}}
unmountOnExit
>
<Cmp {...this.props} />
</CSSTransition>
)
}
}
}
export default withAnimation
# 使用
@withAnimation
class Page extends Component {
render() {
return <div>高阶组件完成路由切换动画效果</div>
}
}
<Route path="/home" children={props => <Page {...props} />} />
  
二十、immutable.js
20.1、概述 官网:https://immutable-js.github.io/immutable-js/ https://cloud.tencent.com/developer/section/1489374 Immutable.js出自Facebook,是最流行的不可变数据结构的实现之一。它实现了完全的持久化数据结构,使用结构共享。所有的更新操作都会返回新的值,但是在内部结构是共享的,来减少内存占用(和垃圾回收的失效)。
持久化数据结构:这里说的持久化是用来描述一种数据结构,指一个数据,在被修改时,仍然能够保持修改前的状态,即不可变类型。
结构共享:Immutable使用先进的tries(字典树)技术实现结构共享来解决性能问题,当我们对一个Immutable对象进行操作的时候,ImmutableJS会只clone该节点以及它的祖先节点,其他保持不变,这样可以共享相同的部分,大大提高性能。
惰性操作:创建的对象时其实代码块没有被执行,只是被声明了,代码在获取或修改的时候才会实际被执行
20.2、使用immutable优缺点
? 优点
降低mutable带来的复杂度
节省内存
历史追溯性
拥抱函数式编程
? 缺点
需要重新学习api
资源包大小增加(源码5000行左右)
容易与原生对象混淆:由于api与原生不同,混用的话容易出错
第3方组件库不支持,使用时,还需要转换
20.3、安装
npm i -S immutable
20.4、常用Api
Map(): 原生object转Map对象
const map1 = Map({ a: 1, b: 2, c: 3})
const map2 = Map({ a: 1, b: 2, c: 3})
console.log(map1 === map2)
console.log(map1.equals(map2))
map.toJS()
--------------------------------------------------------
List(): 原生array转List对象
const list1 = List([1, 2]);
const list2 = list1.push(3, 4, 5);
console.log(list2.get(0));
const list1 = List([ 1, 2, 3 ]);
const list2 = List([ 4, 5, 6 ]);
const list3 = list2.concat(list1);
console.log(list3.toArray())
--------------------------------------------------------
fromJS(): 原生js转immutable对象
const imState = fromJS({
name: 'lisi',
users: ['aa', 'bb']
})
# 获取数据
imState.get('name')
console.log(imState.get('users').get(1))
console.log(imState.getIn(['users', 0]))
--------------------------------------------------------
toJS(): immutable对象转原生js 不推荐使用
const state = imState.toJS()
console.log(state);
--------------------------------------------------------
set/setIn/update/updateIn 修改数据
const newState = imState.set('name', 'zhangsan')
const newState = imState.setIn(['name'], 'zhangsan')
const newState = imState.update('count', value => value + 1)
const newState = imState.updateIn(['count'], value => value + 1)
20.5、redux中集成immutable.js ? 安装redux-immutable redux中利用combineReducers来合并reducer并初始化state,redux自带的combineReducers只支持state是原生js形式的,所以需要使用redux-immutable提供的combineReducers来替换原来的方法。 `
npm i -S redux-immutable
? 使用 它的合并是支持immutable对象合并
import { combineReducers } from 'redux-immutable'
把所有的reducer数据转换为immutable对象
import {fromJS} from 'immutable'
const defaultState = fromJS({})
二十一、项目
一、 **菜谱大全
1.1、项目背景 当下回家吃饭健康饮食的理念正在兴起。据调查显示,有超过九成的都市白领及年轻人其实都倾向于在家里吃饭,尤其是有小孩的家庭意愿会更加强烈, 因为他们普遍都认为在家里吃饭的幸福感会更高; 随着经济的快速发展,人们的生活水平的逐渐提高,对饮食质量要求也越来越高,但都市快节奏的生活让上班族们吃饭的目标性更小,通常只是到了时间随机选 择食物塞饱肚子。该美食网站倡导一种全新的健康的生活方式,用户可以根据网站上提供的食谱了解不同菜系的风格、做法及搭配,除了可以查看各种食谱学习做饭, 还可以在线与其他用户一起交流和分享做菜的心得,通过美食来感受生活之美。 1.2、技术栈
使用react框架来完成本次项目的实现,使用技术有如下一些:
nodejs
react
react-router-dom
redux react-redux redux-thunk immutable redux-immutable
styled-components
antd-mobile
react-transition-group
axios
http-proxy-middleware
配置装饰器(costomize-cra react-app-rewired) 等等
1.3、开发环境
开发环境为:windows
开发工具:vscode/webstorm + jsx插件 + eslint
开发调试工具:chrome浏览器
开发运行环境:node环境
代码管理:git
上线环境:linux + nginx
1.4、项目效果展示 
1.5、项目初始化 ? 在本机磁盘中指定位置创建一下react项目,命令如下
npx create-react-app cookbook
? 创建好项目后,进入项目目录先安装常规要使用的三方包,命令如下
npm i -D customize-cra react-app-rewired http-proxy-middleware
npm i -S redux react-redux redux-thunk styled-components immutable redux-immutable react-router-dom react-transition-group axios
? 清理创建好的项目中不需要的文件及文件夹
* 删除public目录下的部份内容
* 删除src目录下的部份内容
? 修改public/index.html ? 在src目录下创建根组件App.jsx与项目入口文件index.js ? 配置装饰器支持
const {
override,
addDecoratorsLegacy,
addWebpackAlias
} = require("customize-cra");
const path = require("path");
module.exports = override(
addDecoratorsLegacy (),
addWebpackAlias({
["@"]: path.resolve("./src"),
})
);
? 修改package.json中的脚本命令为如下
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
}
? 配置反向代理
const { createProxyMiddleware: proxy } = require("http-proxy-middleware");
module.exports = app => {
app.use(
"/api",
proxy({
target: "http://localhost:9000",
changeOrigin: true,
pathRewrite:{
"^/api": ""
}
})
)
}
二、首页开发
2.1、Ant Design Mobile组件库 在线文档:https://mobile.ant.design/docs/react/introduce-cn antd-mobile是Ant Design的移动规范的 React实现,服务于蚂蚁金服及口碑无线业务。它支持多平台,组件丰富、能全面覆盖各类场景,UI 样式高度可配置,拓展性更强,轻松适应各类产品风格。 ? 在使用前需要先对包进行安装
npm i -S antd-mobile
? 按需加载组件代码和样式的 babel 插件
npm i -D babel-plugin-import
const { override, fixBabelImports } = require('customize-cra')
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd-mobile',
style: 'css',
})
)

? 使用
import React, { Component } from "react";
import { Button } from "antd-mobile";
class App extends Component {
render() {
return (
<>
<Button type="primary">我是一个常规按钮</Button>
</>
);
}
}
export default App;

2.2、底部导航实现 参考地址:https://mobile.ant.design/components/tab-bar-cn/ 底部导航可以使用antd-mobile中的tab-bar组件完成此功能展示。 
 
2.3、顶部导航
height: .4rem;
line-height: .4rem;
background: #FF6C0C;
font-size: .18rem;
text-align: center;
color:#fff;

2.4、轮播显示 参考地址:https://mobile.ant.design/components/carousel-cn 该功能可以使用antd-mobile中的Carousel组件
实现步骤:
在首页组件中引入滑块组件
编写滑块组件实现对应效果

2.5、mock数据 mock数据(faker数据),即使用假数据来模拟后台的数据。 为什么要做假数据?因为后端开发接口并产出接口文档没有哪么快,此时就需要我们自己来模拟请求数据。模拟的数据字段、格式等,需要和后端工程师沟通。这样,我们可以先通过模拟的数据继续完成前端的工作任务,待后端工程师写好数据接口并提供了接口信息后,我们只需要修改请求地址,前后端就实现了无缝衔接。
?安装json-server帮助我们快速启动一个web服务
npm i -g json-server
{
"data": {
"id": 1,
"name": "aaa",
"age": 20
}
}
json-server data.json
? 配置mock ?? 创建src/mock/mock.js文件(文件名并非固定)
const swiper = require("./swiper.json");
module.exports = () => ({
swiper,
});
?? 创建mock的路由文件src/mock/route.json
{
"/api/swiper": "/swiper"
}
? ? 在package.json文件中的scripts中配置启动命令
{
scripts:{
"mock": "json-server ./src/mock/mock.js -r ./src/mock/router.json --port=9090"
}
}
? 启动web服务
npm run mock 
2.6、网络请求封装 封装一下网络请求的方法集合 
反向代理配置一下 
请求配置的请求方法编写 
2.7、搜索组件
export const SearchBox = styled.div`
width: 90vw;
height: .46rem;
display: flex;
border: 1px solid #ff6c0c;
margin: .15rem auto;
border-radius: 5px;
box-shadow: 1px 1px 5px #ccc;
justify-content: center;
align-items: center;
img{
width: .2rem;
height: .2rem;
}
span{
color:#555;
margin-left: .1rem;
}
`

2.8、热门分类
参考地址:https://mobile.ant.design/components/grid-cn/
在antd-mobile组件库中查找到对应的ui组件,使用已存在的组件来简化静态的布局
export const HotCateBox = styled.div`
background: #fff;
.title{
padding: .15rem;
color:#949494;
}
`
<Grid data={hotcate}
columnNum={3}
itemStyle={{ height: '.5rem' }}
onClick={(row, index) => {
console.log(index, this.props.history.push)
}}
renderItem={dataItem => (
<div>{dataItem.title}</div>
)}
/>
2.9、精品好菜 静态布局展示 ?html
<div>
<h1>精品好菜</h1>
<div>
<dl>
<dt>
<img src="http://www.mobiletrain.org/images/index/new_logo.png" />
</dt>
<dd>
<h3>青椒香干</h3>
<p>1000浏览 2000收藏</p>
</dd>
</dl>
</div>
</div>
?css
div{
padding-left: .1rem;
> h1 {
height: .5rem;
line-height: .6rem;
padding-left: .15rem;
color: #666;
padding-left: 0;
}
> div {
display: flex;
flex-wrap: wrap;
> dl {
width: calc(50% - 0.1rem);
background: #fff;
margin-right: .1rem;
margin-bottom: .1rem;
dt {
img {
width: 100%;
}
}
dd {
display: flex;
flex-direction: column;
align-items: center;
padding: .1rem;
h3 {
font-size: .16rem;
}
p {
font-size: .12rem;
color: #666;
}
}
}
}
2.9.1、图片懒加载
npm i -S react-lazyload  
三、分类开发
3.1、分类顶部切换 创建需要的组件和样式 ?html
<ul>
<li class="active">分类</li>
<li>食材</li>
<li class="slider right"></li>
</ul>
?css
div{
height:.44rem;
background:#ee742f;
display:flex;
align-items:center;
justify-content:center;
ul{
width:1.4rem;
height:.3rem;
display:flex;
position:relative;
border:1px solid #fff;
z-index:0;
border-radius:.15rem;
li{
flex:1;
line-height:.3rem;
text-align:center;
color:#fff;
&:last-child{
position:absolute;
width:50%;
height:.3rem;
background:#fff;
z-index:-1;
border-radius:.15rem;
transform:translate(0,0);
transition:all 0.4s ease-in;
&.right{
transform:translate(100%,0);
}
}
&.active{
color:#ee742f;
}
}
}
3.2、列表展示 ?html
<div>
<div>
<ul>
<li class="active"><span>分类</span></li>
</ul>
</div >
<div>
<ul>
<li>内容</li>
</ul>
</div>
</div>
?css
.div{
height:100%;
display:flex;
> div:first-child{
width:.9rem;
> ul{
height:100%;
overflow-y:scroll;
li{
height:.5rem;
text-align:center;
line-height:.5rem;
background:#f3f3f3;
&.active{
background:#fff;
span{
display:inline-block;
height:100%;
border-bottom:1px solid #ee742f;
}
}
}
}
}
>div:last-child{
flex:1;
background:#fff;
padding:.2rem .1rem;
>ul{
display:flex;
flex-wrap:wrap;
overflow-y:scroll;
height:100%;
align-content:flex-start;
li{
width:33.3333%;
text-align:center;
height:.5rem;
line-height:.5rem;
color:#666;
}
}
}
3.2、列表展示 ?html
<div>
<div>
<ul>
<li class="active"><span>分类</span></li>
</ul>
</div >
<div>
<ul>
<li>内容</li>
</ul>
</div>
</div>
?css
.div{
height:100%;
display:flex;
> div:first-child{
width:.9rem;
> ul{
height:100%;
overflow-y:scroll;
li{
height:.5rem;
text-align:center;
line-height:.5rem;
background:#f3f3f3;
&.active{
background:#fff;
span{
display:inline-block;
height:100%;
border-bottom:1px solid #ee742f;
}
}
}
}
}
>div:last-child{
flex:1;
background:#fff;
padding:.2rem .1rem;
>ul{
display:flex;
flex-wrap:wrap;
overflow-y:scroll;
height:100%;
align-content:flex-start;
li{
width:33.3333%;
text-align:center;
height:.5rem;
line-height:.5rem;
color:#666;
}
}
}
上线 配置nginx 
hosts 
四、hook
4.1、简介 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函数的集合,它增强了函数组件的功能,hook不等于函数组件,所有的hook函数都是以use开头。
使用 React Hooks 相比于从前的类组件有以下几点好处:
? 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护
? 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render/Props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现
4.2、使用hook限制
? hook只能用在函数组件中,class组件不行
? 普通函数不能使用hook
? 函数组件内部的函数也不行
? hook定义时要注意先后顺序
? hook函数一定要放在函数组件的第一层,别放在if/for中
4.3、常用hook函数 4.3.1、useState 保存组件状态,功能类似于类组件中的this.state状态管理
import React, { useState } from 'react'
let [count, setCount] = useState(0)
<button onClick={() => setCount(count + 1)}>+++</button>
4.3.2、useEffect 此hook可以模拟函数组件的生命周期,函数组件对于在一些生命周期中操作还是无能为力,所以 React提供了 useEffect 来帮助开发者处理函数组件,来帮助模拟完成一部份的开发中非常常用的生命周期方法。常被别的称为:副作用处理函数。此函数的操作是异步的。
import React, { useState, useEffect } from 'react'
const App = () => {
let [count, setCount] = useState(0)
useEffect(() => {
if (count > 10) {
document.title = count
}
return () => {
}
}, [count]);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>+++</button>
</div>
);
}
4.3.3、useReducer useReducer 这个 Hooks 在使用上几乎跟 Redux一模一样,唯一缺少的就是无法使用 redux 提供的中间件。
import React, { useState, useReducer } from 'react'
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
const App = () => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
<h3>{state.count}</h3>
<button onClick={() => dispatch({ type: 'increment' })}>+++</button>
<button>---</button>
</div>
);
4.3.4、useContext 使用useContext可以方便我们获取Context中的数据源
import React, { useContext } from 'react'
import cxt from '../context/userContext'
const { Provider } = cxt
const App = () => {
return (
<div>
<Provider value='hello'>
<Cmp />
</Provider>
</div>
)
}
function Cmp() {
const context = useContext(cxt)
return (
<h1>
{context}
</h1>
)
}
4.3.5、useMemo 记忆组件,可以理解为计算属性
import React, { useState, useMemo } from 'react';
const Memo = () => {
const [count, setCount] = useState(1)
const [val, setValue] = useState('')
const sum = useMemo(() => {
return count + 10
}, [count]);
return (
<div>
<h3>{count}-{val}-{sum}</h3>
<div>
<button onClick={() => setCount(count + 1)}>+ count</button>
<input value={val} onChange={event => setValue(event.target.value)} />
</div>
</div>
)
}
4.3.6、useCallback 记忆函数,它计算返回一个缓存函数。
import React, { useState, useCallback, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(1);
const [val, setVal] = useState('');
const callback = useCallback(() => {
return count
}, [count])
return (
<div>
<h1>父组件:{count}</h1>
<Child callback={callback} />
<div>
<button onClick={() => setCount(count + 1)}>+count</button>
<input value={val} onChange={event => setVal(event.target.value)} />
</div>
</div>
)
}
function Child({ callback }) {
const [count, setCount] = useState(() => callback())
useEffect(() => {
setCount(callback())
}, [callback]);
return (
<div>
<hr />
<div>子组件:{count}</div>
</div>
)
}
4.3.7、useRef
useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用
import React, { useRef } from 'react'
const Ref = () => {
const username = useRef()
const login = () => {
console.log(username.current.value);
}
return (
<div>
<div>
<input type="text" ref={username} />
<br />
<br />
<button onClick={login}>添加用户</button>
</div>
</div>
)
}
4.3.8、useImperativeHandle 使用它可以透传 Ref,因为函数组件没有实例,所以在默认自定义函数组件中不能使用ref属性,使用为了解决此问题,react提供了一个hook和一个高阶组件完帮助函数组件能够使用ref属性。
import React, { useRef, useEffect, useImperativeHandle, forwardRef } from "react"
function ChildInputComponent(props, useRef) {
useImperativeHandle(ref, () => ({id:1,name:’aaa’}))
return <input type="text" ref={ useRef } />
}
const ChildInput = forwardRef(ChildInputComponent)
function App() {
const inputRef = useRef(null)
const getValue = () => {
console.log(inputRef.current.value)
}
return (
<div>
<ChildInput ref={inputRef} />
<hr/>
<button onClick={getValue}>获取一下数据</button>
</div>
)
}
4.3.9、useLayoutEffect 大部分情况下,使用 useEffect 就可以帮我们处理组件的副作用,但是如果想要同步调用一些副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用会在 DOM 更新之后同步执行。
import React, { useState, useLayoutEffect, useEffect } from 'react'
const Layout = () => {
const [value, setValue] = useState(0)
useLayoutEffect(() => {
const title = document.querySelector("#title")
console.log("useLayoutEffect");
setValue(title.innerHTML);
})
useEffect(() => {
console.log("useEffect")
})
return (
<div>
<div><h1 id="title">hello</h1><h2>{value}</h2></div>
</div>
)
}
4.3.10、react-redux-hook react-redux支持了hook的解决方案,提供两个方法,很方便我们在项目去使用
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
const MyRedux = () => {
const { username, password } = useSelector(state => state.userinfo)
const dispatch = useDispatch()
return (
<div>
<h3>{username}</h3>
<hr />
<button onClick={
e => {
dispatch({
type: 'setname',
name: 'aaaaa'
})
}
}>修改一下姓名</button>
</div>
)
}
4.3.11、react-router-dom-hook react-router-dom也提供了hook的解决方案
import {useHistory,useLocation,useParams} from 'react-router-dom'
4.4、自定义hook 定义自定义hook以use开头 + 函数名称,通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。
import React, {useState, useEffect} from 'react'
function useOnline() {
const [online, setOnline] = useState(navigator.onLine)
useEffect(() => {
const handlerOnline = () => setOnline(true)
const handlerOffline = () => setOnline(false)
window.addEventListener('online', handlerOnline, false)
window.addEventListener('offline', handlerOffline, false)
return () => {
window.removeEventListener('online', handlerOnline, false)
window.removeEventListener('offline', handlerOffline, false)
}
})
return online
}
function App() {
const online = useOnline()
return (
<div>
{
online ?
<h3 style={{color: 'green'}}>在线</h3>
:
<h3>离线了 </h3>
}
</div>
)
}
|