飞翔的鱼 飞翔的鱼
首页
  • Http
  • Ajax
  • Node
  • MongoDB
  • Axios
  • Git
  • Webpack
  • React
  • Vue
  • Uni-app
  • 性能优化
  • 移动端
  • HTML
  • CSS
  • stylus
  • 常用
  • 实战
  • 实用网站
  • 资源库
  • Vue专区
关于
  • css效果
  • js效果
  • 拥抱生活
  • 生活知道
GitHub (opens new window)

Wang

飞翔的鱼
首页
  • Http
  • Ajax
  • Node
  • MongoDB
  • Axios
  • Git
  • Webpack
  • React
  • Vue
  • Uni-app
  • 性能优化
  • 移动端
  • HTML
  • CSS
  • stylus
  • 常用
  • 实战
  • 实用网站
  • 资源库
  • Vue专区
关于
  • css效果
  • js效果
  • 拥抱生活
  • 生活知道
GitHub (opens new window)
  • Http

  • Ajax

  • Node

  • MongoDB

  • Axios

  • Git

  • Webpack

  • React

    • react
      • 1.1. React的基本认识
      • 1.2. React的基本使用
      • 1.3. JSX的理解和使用
      • 1.4概念理解
        • 1). 模块与组件
        • 2). 模块化与组件化
      • 2.1. 基本理解和使用
      • 2.2. 组件的3大属性: state
      • 2.2. 组件的3大属性: props
      • 2.2. 组件的3大属性: refs
      • 2.3. 组件中的事件处理
      • 2.4. 组件的组合使用
      • 2.5. 组件收集表单数据
      • 2.6. 组件的生命周期(旧)
      • 2.7组件的生命周期(新)
      • 2.7. 虚拟DOM与DOM diff算法
        • 1). 虚拟DOM是什么?
        • 2). 虚拟DOM 算法的基本步骤
        • 3). 进一步理解
      • 3.1. 使用create-react-app创建react应用
        • 3.1.1. react脚手架
        • 3.1.2. 创建项目并启动
        • 3.1.3. react脚手架项目结构
      • 4.1. 理解
        • 4.1.1. 前置说明
        • 4.1.2. 常用的ajax请求库
        • 4.1.3. 效果
      • 4.2. axios
        • 4.2.1. 文档
        • 4.2.2. 相关API
      • 4.3. Fetch
        • 4.3.1. 文档
        • 4.3.2. 相关API
      • 5.1. 组件间通信
        • 5.1.1. 方式一: 通过props传递
        • 5.1.2. 方式二: 使用消息订阅(subscribe)-发布(publish)机制
        • 5.1.3. 方式三: redux
        • 5.1.4. 面试题: 比较react中组件间3种通信方式
      • 5.2. 事件监听理解
        • 5.2.1. 原生DOM事件
        • 5.2.2. 自定义事件(消息机制)
      • 5.3. ES6常用新语法
      • 6.1. 相关理解
        • 6.1.1. react-router-dom的理解
        • 6.1.2. SPA的理解
        • 6.1.3. 路由的理解
        • 6.1.4. 前端路由的实现
      • 6.2. react-router相关API
        • 6.2.1. 组件
        • 6.2.2. 其它
      • 6.3. 基本路由使用
        • 6.3.1. 准备
        • 6.3.2. 路由组件: pages/about.jsx
        • 6.3.3. 路由组件: pages/home.jsx
        • 6.3.4.BrowserRouter
        • 6.3.5. 应用组件:app.jsx
        • 6.3.6. 自定义样式: index.css
      • 6.4. 嵌套路由使用
        • 6.4.2. 二级路由组件: pages/news.jsx
        • 6.4.3. 二级路由组件: pages/message.jsx
        • 6.4.4. 一级路由组件: pages/home.jsx
      • 6.5. 向路由组件传递参数数据
        • 6.5.1. 三级路由组件: pages/home-message-detail.jsx
        • 6.5.2. 二级路由组件: pages/home-message.jsx
      • 6.6. 多种路由跳转方式
        • 6.6.1. 二级路由: views/message.jsx
      • 7.1. 最流行的开源React UI组件库
        • 7.1.1. material-ui(国外)
        • 7.1.2. ant-design(国内蚂蚁金服)
      • 7.2. ant-design使用入门
        • 1.引入antd
        • 2.下载组件库包
        • 3.实现组件的按需打包
        • 4. 自定义antd主题
        • 5.在应用组件中使用antd
      • 8.1. redux理解
        • 8.1.1. 学习文档
        • 8.1.2. redux是什么?
        • 8.1.3. 什么情况下需要使用redux
      • 8.2. redux的核心API
        • 8.2.1. createStore()
        • 8.2.2. store对象
        • 8.2.3. applyMiddleware()
        • 8.2.4. combineReducers()
      • 8.3. redux的三个核心概念
        • 8.3.1. action
        • 8.3.2. reducer
        • 8.3.3. store
      • 8.4. 使用redux编写应用
        • 8.4.1. 效果
        • 8.4.2. 下载依赖包
        • 8.4.3. redux/action_types.js
        • 8.4.4. redux/actions_creators.js
        • 8.4.5. redux/reducers.js
        • 8.4.6. components/app.jsx
        • 8.4.7. store.js
        • 8.4.8. 问题
      • 8.5. react-redux
        • 8.5.1. 理解
        • 8.5.2. React-Redux将所有组件分成两大类
        • 8.5.3. 相关API
        • 8.5.4. 使用react-redux
        • 8.5.5. 问题
      • 8.6. redux异步编程
        • 8.6.1. 下载redux插件(异步中间件)
        • 8.6.2. store.js
        • 8.6.3. redux/actions.js
        • 8.6.4. components/counter.jsx
        • 8.6.5. containers/app.jsx
      • 8.7. 使用上redux调试工具
        • 8.7.1. 安装chrome浏览器插件
        • 8.7.2. 下载工具依赖包
        • 8.7.3. 编码
      • 8.8. 相关重要知识: 纯函数和高阶函数
        • 8.8.1. 纯函数
        • 8.8.2. 高阶函数
  • Vue

  • Uni-app

  • 性能优化

  • 移动端

  • 前端
  • React
wang jiaqi
2021-03-17

react

# 第一章. React入门

# 1.1. React的基本认识

1). Facebook开源的一个js库
2). 一个用来动态构建用户界面的js库
3). React的特点
	Declarative(声明式编码)
	Component-Based(组件化编码)
	Learn Once, Write Anywhere(支持客户端与服务器渲染)
	高效
	单向数据流
4). React高效的原因
	虚拟(virtual)DOM, 不总是直接操作DOM(批量更新, 减少更新的次数) 
	高效的DOM Diff算法, 最小化页面重绘(减小页面更新的区域)
	One Dark Pro
	Auto Rename Tag
	Bracket Pair Colorizer 2

# 1.2. React的基本使用

1). 导入相关js库文件(react.js, react-dom.js, babel.min.js)
2). 编码:
	<div id="app"></div>
	<script type="text/javascript">
    let vDom = 'hello'  
    ReactDOM.render(vDom,document.getElementById("app"))
  </script>
1
2
3
4
5
6
7

# 1.3. JSX的理解和使用

1). 理解
	* 全称: JavaScript XML
	* react定义的一种类似于XML的JS扩展语法: XML+JS
	* 作用: 用来创建react虚拟DOM(元素)对象
2). 编码相关
  	<div id="test"></div>
---------------------------------------
	<script type="text/babel">		//类型为text/babel
    let myID = 'id'
    let myData = 'hello,world'
    //1.创建虚拟DOM
    let vDOM2 = <h2 id={myID}><span>{myData}</span></h2>
    //2.渲染虚拟DOM到页面
    ReactDOM.render(vDOM2,document.getElementById('test'))
  </script>
3). 注意:
		(1)标签的class属性必须改为className属性
        (2)必须要有根标签
        (3)标签中js语法用{}包起来,{}中的js必须是表达式
     	(4)标签必须有结束
		(5)标签的style属性值必须为: {{color:'red', width:12}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 1.4概念理解

# 1). 模块与组件

1. 模块:
  	理解: 向外提供特定功能的js程序, 一般就是一个js文件
  	为什么: js代码更多更复杂
  	作用: 复用js, 简化js的编写, 提高js运行效率
2. 组件: 
	理解: 用来实现特定界面功能效果的代码集合(html/css/js/img)
 	为什么: 一个界面的功能太复杂了
  	作用: 复用编码, 简化项目界面编码, 提高运行效率

# 2). 模块化与组件化

1. 模块化:
	当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
2. 组件化:
	当应用是以多组件的方式实现功能, 这上应用就是一个组件化的应用

# 第二章. react组件化开发

# 2.1. 基本理解和使用

//组件名首字母必须大写
1). 创建组件类
	//方式1: 工厂函数(简单组件, 推荐使用)
	function MyComponent1() {
		return <h1>工厂函数</h1>
	}
	//方式2: ES6类语法(复杂组件, 推荐使用)
	class MyComponent2 extends React.Component {
		render () {
		  return <h1>ES6类语法</h1>
		}
	}
2). 渲染组件标签
	ReactDOM.render(<MyComponent1/>,  document.getElementById("app"))
	ReactDOM.render(<MyComponent2/>,  document.getElementById("app"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.2. 组件的3大属性: state

(所有属性针对的是组件实例对象)

1. 组件被称为"状态机", 页面的显示是根据组件的state属性的数据来显示
2. 初始化指定:
    state = {isPig:true}		//state以键值对的形式存放数据
3. 读取显示: 
    let {isPig}=this.state		//结构赋值形式取state值
4. 更新状态-->更新界面 : 
    this.setState({isPig : false})	//以键值对形式
1
2
3
4
5
6
7

实例代码

class Person extends React.Component {
        state = { isP: true };
        demo = () => {	//自定义的方法必须用馒头函数,可以解决this问题
          let isP = !this.state.isP;
          this.setState({ isP });	//state不能直接更新,通过setState更新
        };
        render() {
          let { isP } = this.state;
            //React中的事件名需要大写,需要用this
          return <h1 onClick={this.demo}>你是{isP ? "人" : "猪"}</h1>;
        } 
      }
ReactDOM.render(<Person />, document.getElementById("test"));
1
2
3
4
5
6
7
8
9
10
11
12
13

注意事项

//1.state中的数据不能直接修改
this.state=!this.state	//错误做法
this.setState({})		//正确
//2.如果state中的数据是对象类型,不能直接更新状态
let {comment}=this.state	//错误,修改了原数组中对象
let comment=[...this.state.comment]		//正确,使用es8的...运算符,创建了一个新的数组
1
2
3
4
5
6

# 2.2. 组件的3大属性: props

所有组件标签的属性的集合对象
给标签指定属性, 保存外部数据(可能是一个function)
在组件内部读取属性: this.props.propertyName
作用: 从目标组件外部向组件内部传递数据
对props中的属性值进行类型限制和必要性限制
	Person.propTypes = {
		name: React.PropTypes.string.isRequired,
		age: React.PropTypes.number.isRequired
	}
扩展属性: 将对象的所有属性通过props传递

    <Person {...person}/>

实例对象

class Person extends React.Component{
    //类里面的方法一旦加上static关键字,该方法只能有类本身调用,类的实例对象不能使用。
    //限制接收参数的类型以及必要性
    static propTypes = {
      name:PropTypes.string.isRequired,
      sex:PropTypes.string.isRequired,
      age:PropTypes.number
    }
    //设置参数的默认值
    static defaultProps = {
      age:18
    }
    //重写父类的render方法
    render(){
      let {name,age,sex} = this.props
      return (
        <ul>
          <li>姓名:{name}</li>
          <li>性别:{age+1}</li>
          <li>年龄:{sex}</li>
          <hr/>
        </ul>
      )
    }
  }
  
  
  let p2 = {
    name:'张三',
    age:19,
    sex:'男'
  }

 
  //2.渲染组件标签,利用react内部的...obj属性获取组件
  ReactDOM.render(<Person {...p2}/>,document.getElementById('example2'))
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

组件中的props

//定义状态
state={
    comment:[
      {id:"ggttjjja",name:"jack",content:"简单"},
      {id:"dddghhja",name:"bar",content:"不难"},
      {id:"dsadwt03",name:"foo",comment:"so easy"}
    ]
  }
//List传参
<List comment={comment}/>
-------------------------
//接收参数
let { comment } = this.props
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.2. 组件的3大属性: refs

作用: 找到组件内部的真实dom元素对象, 进而操作它
三种使用方式
	方式一:ref="input1"  //字符串形式
	方式二:ref={input => this.input1 = input}//回调函数形式
	方式三:myRef = React.createRef()//创建一个ref容器形式
1
2
3
4
5

示例代码:方式一

class UserInput extends React.Component {
        handlerClick = () => {
          //通过ref获取DOM
          let { input1 } = this.refs;
          alert(input1.value);
        };
        render() {
          return (
            <div>
              //方式一:
              <input type="text" ref="input1" />
              &nbsp;
              <button onClick={this.handlerClick}>弹出数据</button>&nbsp;
            </div>
          );
        }
      }
ReactDOM.render(<UserInput />, document.getElementById("example")); 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

方式二

class UserInput extends React.Component {
        handlerClick = () => {
          //通过ref获取DOM
          alert(this.input1.value);
        };
        render() {
          return (
            <div>
              {/* 方式二 inptu为形参,自定义,inptut1为方法名 */}
              <input type="text" ref={(input) => (this.input1 = input)} />
              <button onClick={this.handlerClick}>弹出数据</button>&nbsp;
            </div>
          );
        }
      }
ReactDOM.render(<UserInput />, document.getElementById("example"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

方式三

 class UserInput extends React.Component {
        //方式三:创建一个ref容器
        //这个容器是一个“专用容器”,只能保存一个元素
        myRef = React.createRef();
        handlerClick = () => {
          //通过ref获取DOM,需要通过current属性获取value
          let { current } = this.myRef;
          alert(current.value);
        };
        render() {
          return (
            <div>
              {/*将当前元素放入组件对象身上的myRef容器中*/}
              <input type="text" ref={this.myRef} />
              <button onClick={this.handlerClick}>弹出数据</button>&nbsp;
            </div>
          );
        }
      }
ReactDOM.render(<UserInput />, document.getElementById("example"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.3. 组件中的事件处理

1. 给标签添加属性: onXxx={this.eventHandler}
2. 在组件中添加事件处理方法
    eventHandler = (event) => {
                
    }
3. 使自定义方法中的this为组件对象
  	在constructor()中bind(this)
  	使用箭头函数定义方法
4. 事件监听
	绑定事件监听
		事件名
		回调函数
	触发事件
		用户对对应的界面做对应的操作
		编码
 5.注意:在调用函数是需要传参时要放在函数里面
 onXxx={()=>{this.eventHandler(id)}}
 eventHandler = (id) => {
                
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.4. 组件的组合使用

1)拆分组件: 拆分界面,抽取组件
2)实现静态组件: 使用组件实现静态页面效果
3)实现动态组件
	① 动态显示初始化数据
	② 交互功能(从绑定事件监听开始)

# 2.5. 组件收集表单数据

受控组件: 输入过程中自动收集数据到state
非受控组件: 提交时手动读取数据

# 2.6. 组件的生命周期(旧)

1. 组件的三个生命周期状态:
	Mount:插入真实 DOM
	Update:被重新渲染
	Unmount:被移出真实 DOM
2. 生命周期流程:
	* 第一次初始化显示: ReactDOM.render(<Xxx/>, containDom)
		constructor()
		componentWillMount() : 将要插入回调
		render() : 用于插入虚拟DOM回调
		componentDidMount() : 已经插入回调
	* 每次更新state: this.setState({})
	    componentWillReceiveProps(): 接收父组件新的属性
	    componentWillUpdate() : 将要更新回调
	    render() : 更新(重新渲染)
	    componentDidUpdate() : 已经更新回调
	* 删除组件: ReactDOM.unmountComponentAtNode(div): 移除组件
		componentWillUnmount() : 组件将要被移除回调
3. 常用的方法
  	componentDidMount() : 只执行一次, 已经在dom树中, 适合启动/设置一些监听
  	componentWillUnmount():适合做收尾工作,清楚定时器/关闭数据库/关闭流

# 2.7组件的生命周期(新)

  1. 组件的三个生命周期状态:
    

* 初始化阶段:第一次渲染,插入真实DOM

* 更新阶段:重新渲染

* 卸载阶段:被移出真实 DOM

  1. 生命周期流程:

a. 初始化阶段: 由ReactDOM.render()触发

* constructor()

* static getDerivedStateFromProps()

* render()

* componentDidMount()

b. 更新阶段 由组件内部this.setSate()或父组件重新render触发

* static getDerivedStateFromProps()

* shouldComponentUpdate()

* render()

* getSnapshotBeforeUpdate()

* componentDidUpdate()

c. 移除组件: 由ReactDOM.unmountComponentAtNode(containerDom)触发

* componentWillUnmount()

重要的勾子

  1. render(): 初始化渲染或更新渲染调用
    
  2. componentDidMount(): 开启监听, 发送ajax请求
    
  3. componentWillUnmount(): 做一些收尾工作, 如: 清理定时器
    
  4. static getDerivedStateFromProps(): state是根据props来生成的那就需要借助这个函数
    

# 2.7. 虚拟DOM与DOM diff算法

# 1). 虚拟DOM是什么?

一个虚拟DOM(元素)是一个一般的js对象, 准确的说是一个对象树(倒立的)
虚拟DOM保存了真实DOM的层次关系和一些基本属性,与真实DOM一一对应
如果只是更新虚拟DOM, 页面是不会重绘的

# 2). 虚拟DOM 算法的基本步骤

用JS对象树表示DOM树的结构;然后用这个树构建一个真正的DOM树插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把差异应用到真实DOM树上,视图就更新了

# 3). 进一步理解

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。

# 第3章:react应用(基于react脚手架)

# 3.1. 使用create-react-app创建react应用

# 3.1.1. react脚手架

  1. xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
    

a. 包含了所有需要的配置

b. 指定好了所有的依赖

c. 可以直接安装/编译/运行一个简单效果

  1. react提供了一个用于创建react项目的脚手架库: create-react-app
    
  2. 项目的整体技术架构为: react + webpack + es6 + eslint
    
  3. 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
    

# 3.1.2. 创建项目并启动

npm install -g create-react-app		//全局下载react脚手架

create-react-app hello-react		//创建react项目,hello-react为项目名

cd hello-react						//切换到项目目录

npm start							//运行项目
1
2
3
4
5
6
7

# 3.1.3. react脚手架项目结构

ReactNews

|--node_modules---第三方依赖模块文件夹

|--public

|-- *index.html-----------------*主页面

|--src------------源码文件夹

|--components-----------------react**组件

|--index.js-------------------应用入口js

|--.gitignore------git版本管制忽略的配置

|--package.json----应用包配置文件

|--README.md-------应用描述说明的readme文件

# 第4章:react ajax

# 4.1. 理解

# 4.1.1. 前置说明

  1. React本身只关注于界面, 并不包含发送ajax请求的代码
    
  2. 前端应用需要通过ajax请求与后台进行交互(json数据)
    
  3. react应用中需要集成第三方ajax库(或自己封装)
    

# 4.1.2. 常用的ajax请求库

  1. jQuery: 比较重, 如果需要另外引入不建议使用
    
  2. axios: 轻量级, 建议使用
    

a. 封装XmlHttpRequest对象的ajax

b. promise风格

c. 可以用在浏览器端和node服务器端

  1. fetch: 原生函数, 但老版本浏览器不支持
    

a. 不再使用XmlHttpRequest对象提交ajax请求

b. 为了兼容低版本的浏览器, 可以引入兼容库fetch.js

# 4.1.3. 效果

需求:
  1. 界面效果如下
  2. 根据指定的关键字在github上搜索匹配的最受关注的库
  3. 显示库名, 点击链接查看库
4. 测试接口: https://api.github.com/search/repositories?q=r&sort=stars

1
2
3
4
5
6

# 4.2. axios

# 4.2.1. 文档

https://github.com/axios/axios

# 4.2.2. 相关API

  1. GET请求
    
axios
  .get("/user?ID=12345")
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.log(error);
  });
---------------------------------------
axios
  .get("/user", { params: { ID: 12345 } })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  1. POST请求
    
axios
.post("/user", { firstName: "Fred", lastName: "Flintstone" })
.then(function (response) {
  console.log(response);
})
.catch(function (error) {
  console.log(error);
});

1
2
3
4
5
6
7
8
9

# 4.3. Fetch

# 4.3.1. 文档

  1. https://github.github.io/fetch/
    
  2. https://segmentfault.com/a/1190000003810652
    

# 4.3.2. 相关API

  1. GET请求
    
fetch(url)
.then(function (response) {
  return response.json();		//返回一个promise实例对象,状态为<pending>
})
.then(function (value) {
  console.log(value);	//成功
})
.catch(function (reason) {
  console.log(e);		//失败
});
1
2
3
4
5
6
7
8
9
10
  1. POST请求
    
fetch(url, { method: "POST", body: JSON.stringify(data) })
 .then(function (response) {
   return response.json();		//返回一个promise实例对象,状态为<pending>
 })
 .then(function (value) {
   console.log(value);	//成功
 })
 .catch(function (reason) {
   console.log(e);		//失败
 });
1
2
3
4
5
6
7
8
9
10
  1. fetch的问题
    
url:  https://api.github.com/search/repositories
.then(function (response) {
    return response.json();		//在此方法中如果https://api.github.com有错误,调用catch,但是search/repositories有错误,则调用的是then,显然不能得到数据。解决方案:自己简单对response进行封装
  })
1
2
3
4

# 第5章:几个重要技术总结

# 5.1. 组件间通信

# 5.1.1. 方式一: 通过props传递

  1. 共同的数据放在父组件上, 特有的数据放在自己组件内部(state)
    
  2. 通过props可以传递一般属性和函数属性, 只能一层一层传递
    
  3. 一般属性-->父组件传递数据给子组件-->子组件读取数据
    
  4. 函数属性-->子组件传递数据给父组件-->子组件调用函数
    

# 5.1.2. 方式二: 使用消息订阅(subscribe)-发布(publish)机制

1)     工具库: PubSubJS

2)     下载: npm install pubsub-js 

3)     使用: 

   import PubSub from 'pubsub-js' //引入

   PubSub.subscribe('delete', (msg,data)=>{}); //订阅消息,一般放在钩子里,delete是标识,与发布相匹配,data是真正数据

   PubSub.publish('delete', data) //发布消息,data是发布的数据,与上面的相对应
1
2
3
4
5
6
7
8
9
10
11

示例代码:

//List组件,订阅消息
componentDidMount(){
        Pubsub.subscribe('hello',(msg,data)=>{
          this.setState(data)
      })
  }
//Add组件,发布消息
Pubsub.publish('hello',{
          users:[],
          isFirst:false,
          isLoading:true,
          error:""
        })
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5.1.3. 方式三: redux

后面专门讲解

# 5.1.4. 面试题: 比较react中组件间3种通信方式

*1).* *方式一**:* *通过**props**传递   通过**props**可以传递一般数据和函数数据**,  *  *一般数据**-->**父组件向子组件   函数数据**-->**子组件向父组件通信   缺点**:* *只能一层一层传递**/**兄弟组件必须借助父组件 *  *2).* *方式二**:* *使用消息订阅**(subscribe)-**发布**(publish)**机制   实现库**: pubsub-js *  *组件**A:* *发布消息**(**相当于触发事件**) *  *组件**B:* *订阅消息**,* *接收消息**,* *处理消息**(**相当于绑定事件监听**) *  *优点**:* *对组件关系没有限制 *  *3).* *方式三**: redux *  *通过**redux**可以实现任意组件间的通信   集中式管理多个组件共享的状态**,* *而**pubsub-js**并不是集中式的管理*
1

# 5.2. 事件监听理解

# 5.2.1. 原生DOM事件

  1. 绑定事件监听 
    

a. 目标元素

b. 事件名(类型): 只有有限的几个, 不能随便写

c. 回调函数: 接收数据并处理

  1.     触发事件
    

a. 用户操作界面

b. 事件名(类型)

c. 数据(event)

# 5.2.2. 自定义事件(消息机制)

  1. 绑定事件监听(订阅消息)
    

a. 目标元素

b. 事件名(类型): 任意

c. 回调函数: 通过形参接收数据, 在函数体处理事件

  1. 触发事件(编码, 发布消息)
    

a. (事件名类型): 与绑定的事件监听的事件名一致

b. 数据: 会自动传递给回调函数

# 5.3. ES6常用新语法

  1. 定义常量/变量: const/let
    
  2. 解构赋值: let {a, b} = this.props  import {aa} from 'xxx' function fn({name}) {} fn({name: ‘tom’})
    
  3. 对象的简洁表达: {a, b}
    
  4. 箭头函数: 
    

a. 常用场景

* 组件的自定义方法: xxx = () => {}

* 参数匿名函数

b. 优点:

​ * 简洁

​ * 没有自己的this,使用引用this查找的是外部this

  1. 扩展(三点)运算符: 拆解对象(const MyProps = {}, <Xxx {...MyProps}>)
    
  2. 类: class/extends/constructor/super
    
  3. ES6模块化: export default | import

  4. promise

  5. async/await

# 第6章:react-router4

# 6.1. 相关理解

# 6.1.1. react-router-dom的理解

  1. 	react的一个插件库
    
  2.     专门用来实现一个SPA应用
    
  3.     基于react的项目基本都会用到此库
    

# 6.1.2. SPA的理解

  1. 	单页Web应用(single page web application,SPA)
    
  2.     整个应用只有一个完整的页面
    
  3.     点击页面中的链接不会刷新页面, 本身也不会向服务器发请求
    
  4.     当点击路由链接时, 只会做页面的局部更新
    
  5.     数据都需要通过ajax请求获取, 并在前端异步展现
    

# 6.1.3. 路由的理解

  1. 什么是路由?
    

a. 一个路由就是一个映射关系(key:value)

b. key为路由路径, value可能是function/component

  1. 路由分类
    

a. 后台路由: node服务器端路由, value是function, 用来处理客户端提交的请求并返回一个响应数据

b. 前台路由: 浏览器端路由, value是component, 当请求的是路由path时, 浏览器端前没有发送http请求, 但界面会更新显示对应的组件

  1. 后台路由
    

a. 注册路由: router.get(path, function(req, res))

b. 当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

  1.     前端路由
    

a. 注册路由:

b. 当浏览器的hash变为/about时, 当前路由组件就会变为About组件

# 6.1.4. 前端路由的实现

  1. history库
    

a. 网址: https://github.com/ReactTraining/history

b. 管理浏览器会话历史(history)的工具库

c. 包装的是原生BOM中window.history和window.location.hash

  1. history API
    

a. History.createBrowserHistory(): 得到封装window.history的管理对象

b. History.createHashHistory(): 得到封装window.location.hash的管理对象

c. history.push(): 添加一个新的历史记录

d. history.replace(): 用一个新的历史记录替换当前的记录

e. history.goBack(): 回退到上一个历史记录

f. history.goForword(): 前进到下一个历史记录

g. history.listen(function(location){}): 监视历史记录的变化

# 6.2. react-router相关API

# 6.2.1. 组件

  1. <BrowserRouter>
    
  2. <HashRouter>
    
  3. <Route>
    
  4. <Redirect>
    
  5. <Link>
    
  6. <NavLink>
    
  7. <Switch>
    

# 6.2.2. 其它

  1. history对象
    
  2. match对象
    
  3. withRouter函数
    

# 6.3. 基本路由使用

# 6.3.1. 准备

yarn add react-router-dom		// 下载react-router-dom 
1

# 6.3.2. 路由组件: pages/about.jsx

import React, { Component } from "react";
class About extends Component {
    render() {
      return (
        <h3>我是About的内容</h3>
      );
    }
  }
  
  export default About;
1
2
3
4
5
6
7
8
9
10

# 6.3.3. 路由组件: pages/home.jsx

import React, { Component } from "react";
class Home extends Component {
    render() {
      return (
        <h3>我是Home的内容</h3>
      );
    }
  }
  
  export default Home;
1
2
3
4
5
6
7
8
9
10

# 6.3.4.BrowserRouter

import {BrowserRouter} from 'react-router-dom'
 <BrowserRouter>
    <App />
 </BrowserRouter>
//需要在index.js里面的App组件外面加上BrowserRouter才能使得NavLink生效
注意:在引入要样式时不要前面的点或者写成%PUBLIC_URL%这样形式的
<link rel="stylesheet" href="%PUBLIC_URL%/bootstrap.css" />
1
2
3
4
5
6
7

# 6.3.5. 应用组件:app.jsx

import React, { Component } from "react";
import {  NavLink, Redirect, Route, Switch } from "react-router-dom";
import About from './pages/about'
import Home from './pages/home'
<div className="list-group">
 <NavLink className="list-group-item" to="/about" activeClassName="demo">About</NavLink>
 <NavLink className="list-group-item" to="/home" activeClassName="demo">Home</NavLink>   </div>
//NavLink为路由链接,to是跳转到地方,前面需要加/
//activeClassName是选中时的样式
--------------------------------------------
<Switch>
   <Route path="/about" component={About}></Route>
   <Route path="/home" component={Home}></Route>
   <Redirect to="/about"></Redirect>
</Switch>
//Switch是避免路由一直匹配,当匹配到路由时就停止
//Route是路由,path是匹配的路由路径
//Redirect是重定向,当不指定路由是时,默认的为Redirect
//component={About}引入组件的另一种方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 6.3.6. 自定义样式: index.css

.demo {
        background-color: gray !important;
        color: orange !important;
 }
1
2
3
4

# 6.4. 嵌套路由使用

# 6.4.2. 二级路由组件: pages/news.jsx

import React, { Component } from "react";

class HomeNews extends Component {
  render() {
    return (
      <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
      </ul>
    );
  }
}

export default HomeNews;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 6.4.3. 二级路由组件: pages/message.jsx

import React, { Component } from "react";

class HomeMessage extends Component {
  render() {
    return (
      <div>
        <ul>
          <li> 
              <button>replace查看</button>
          </li>
        </ul>
      </div>
    );
  }
}

export default HomeMessage;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 6.4.4. 一级路由组件: pages/home.jsx

import React, { Component } from "react";
import { NavLink, Redirect, Route, Switch } from "react-router-dom";
import HomeNews from './home_news'
import HomeMessage from './home_message'
class Home extends Component {
    render() {
        return (
            <div><h2>Home组件内容</h2>
                <div>
                    <ul className="nav nav-tabs">
                        <li>
                            <NavLink className="list-group-item" to="/home/news" activeClassName="selected">News</NavLink>
                        </li>
                        <li>
                            <NavLink className="list-group-item " to="/home/message" activeClassName="selected">Message</NavLink>
                        </li>
                    </ul>
                    
                    <Switch>
                        <Route path="/home/news" component={HomeNews}></Route>
                        <Route path="/home/message" component={HomeMessage}></Route>
                        <Redirect to="/home/news"></Redirect>
                    </Switch>
                </div>
            </div>
        );
    }
}
export default Home;
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

# 6.5. 向路由组件传递参数数据

# 6.5.1. 三级路由组件: pages/home-message-detail.jsx

import React, { Component } from 'react'
export default class HomeMessageDetail extends Component {
    state = {
        messageDetail: [
            { id: 1, title: '消息1', content: '内容1' },
            { id: 2, title: '消息2', content: '内容2' },
            { id: 3, title: '消息3', content: '内容3' },
        ]
    }
    render() {
        
        // console.log('我是hmd的实例',this.props.match.params);
        let { id } = this.props.match.params
        //通过组件实例对象this.props.match.params上取得参数(id)
        //注意:通过实例取得的参数是字符串,需要转换为Number类型与之匹配
        let { messageDetail } = this.state
        let obj = messageDetail.find((item) => {
            return item.id === id * 1
        })
        if (obj) {
            return (
                <ul>
                    <li>id:{obj.id}</li>
                    <li>title:{obj.til}</li>
                    <li>content:{obj.content}</li>
                </ul>
            )
        } else {
            return <h1>没有消息</h1>
        }

    }
}
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

# 6.5.2. 二级路由组件: pages/home-message.jsx

import React, { Component } from "react";
import { Link, Route } from "react-router-dom"
import HomeMessageDetail from './home_message_detail'
class HomeMessage extends Component {
  state = {
    message: []
  }
  componentDidMount() {
    setInterval(() => {
      this.setState({
        message: [
          { id: 1, title: "message1" },
          { id: 2, title: "message2" },
          { id: 3, title: "message3" }
        ]
      })
    }, 1000)
  }
  render() {

    let { message } = this.state
    return (
      <div>
        <ul>
          {message.map((item) => {
            return (
              <li key={item.id}>
                <Link to={`/home/message/detail/${item.id}`}>{item.title}</Link>
                   //在/home/message/detail/后面传递参数
              </li>
            )
          })
          }

        </ul>

        <hr />
        <Route path="/home/message/detail/:id" component={HomeMessageDetail}></Route>
            //在/home/message/detail/后面通过占位符接收参数(一般为id)
      </div>
    );
  }
}

export default HomeMessage;

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
39
40
41
42
43
44
45
46

# 6.6. 多种路由跳转方式

# 6.6.1. 二级路由: views/message.jsx

import React, { Component } from "react";
import { Link, Route } from "react-router-dom"
import HomeMessageDetail from './home_message_detail'
class HomeMessage extends Component {
  state = {
    message: []
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        message: [
          { id: 1, title: "message1" },
          { id: 2, title: "message2" },
          { id: 3, title: "message3" }
        ]
      })
    }, 1000)

  }
  push = (id) => {
    this.props.history.push(`/home/message/detail/${id}`)
  }
  replace = (id) => {
    this.props.history.replace(`/home/message/detail/${id}`)
  }
  render() {

    let { message } = this.state
    return (
      <div>
        <ul>
          {message.map((item) => {
            return (
              <li key={item.id}>
                <Link to={`/home/message/detail/${item.id}`}>{item.title}</Link>
                <button onClick={() => { this.push(item.id) }}>push</button>
                <button onClick={() => { this.replace(item.id) }}>replace</button>
				//通过button方式跳转路由,原理就是借助History库通过改变改变地址栏中的url来实现
              </li>
            )
          })
          }

        </ul>

        <hr />
        <Route path="/home/message/detail/:id" component={HomeMessageDetail}></Route>
      </div>
    );
  }
}

export default HomeMessage;

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 第7章:react UI组件库

# 7.1. 最流行的开源React UI组件库

# 7.1.1. material-ui(国外)

  1.     官网: http://www.material-ui.com/#/
    
  2.     github: https://github.com/callemall/material-ui
    

# 7.1.2. ant-design(国内蚂蚁金服)

  1.     PC官网: https://ant.design/index-cn
    
  2. 移动官网: https://mobile.ant.design/index-cn
    
  3.     Github: https://github.com/ant-design/ant-design/
    
  4. Github: https://github.com/ant-design/ant-design-mobile/
    

# 7.2. ant-design使用入门

# 1.引入antd

参考文档:

https://ant.design/docs/react/use-with-create-react-app-cn

# 2.下载组件库包

yarn add antd
1

# 3.实现组件的按需打包

  1. 下载依赖模块
yarn add react-app-rewired customize-cra babel-plugin-import
1
  1. 在根目录下创建加载配置的js模块: config-overrides.js
const {override, fixBabelImports} = require('customize-cra');
module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
  }),
);
1
2
3
4
5
6
7
8

3) 修改配置: package.json

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

# 4. 自定义antd主题

  1. 下载工具包:
yarn add less@3.9.0 less-loader@4.1.0		//less版本和less-loader需要匹配
1
  1. 修改config-overrides.js
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'},    
  })
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 5.在应用组件中使用antd

import { Button } from 'antd';
render() {
    return (
      <div>
        App
        <Button type="primary">Button</Button>
      </div>
    );
}
1
2
3
4
5
6
7
8
9

# 第8章 Redux

# 8.1. redux理解

# 8.1.1. 学习文档

  1. 英文文档: https://redux.js.org/
    
  2. 中文文档: http://www.redux.org.cn/
    
  3. Github: https://github.com/reactjs/redux
    

# 8.1.2. redux是什么?

  1. redux是一个独立专门用于做状态管理的JS库(不是react插件库)
    
  2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用
    
  3. 作用: 集中式管理(读/写)react应用中多个组件共享的状态
    

# 8.1.3. 什么情况下需要使用redux

  1. 总体原则: 能不用就不用, 如果不用比较吃力才考虑使用
    
  2.     某个组件的状态,需要共享
    
  3.     某个状态需要在任何地方都可以拿到
    
  4.     一个组件需要改变全局状态
    
  5.     一个组件需要改变另一个组件的状态
    

# 8.2. redux的核心API

# 8.2.1. createStore()

  1. 作用: 
    

创建包含指定reducer的store对象

  1. 编码:
    

import {createStore} from 'redux'

import counter from './reducers/counter'

const store = createStore(counter)

# 8.2.2. store对象

  1. 作用: 
    

redux库最核心的管理对象

  1. 它内部维护着:
    

​ state

​ reducer

  1. 核心方法:
    

​ getState()

​ dispatch(action)

​ subscribe(listener)

  1. 编码:
    

​ store.getState()

​ store.dispatch({type:'INCREMENT', number})

​ store.subscribe(render)

# 8.2.3. applyMiddleware()

  1. 作用:
    

应用上基于redux的中间件(插件库)

  1. 编码:
    

import {createStore, applyMiddleware} from 'redux'

import thunk from 'redux-thunk' // redux异步中间件

const store = createStore(

counter,

applyMiddleware(thunk) // 应用上异步中间件

)

# 8.2.4. combineReducers()

  1. 作用:
    

合并多个reducer函数

  1. 编码:
    

export default combineReducers({

user,

chatUser,

chat

})

# 8.3. redux的三个核心概念

# 8.3.1. action

  1. 标识要执行行为的对象
    
  2. 包含2个方面的属性
    

a. type: 标识属性, 值为字符串, 唯一, 必要属性

b. xxx: 数据属性, 值类型任意, 可选属性

  1. 例子:
    

​ const action = {

​ type: 'INCREMENT',

​ data: 2

​ }

  1. Action Creator(创建Action的工厂函数)
    

​ const increment = (number) => ({type: 'INCREMENT', data: number})

# 8.3.2. reducer

  1. 根据老的state和action, 产生新的state的纯函数
    
  2. 样例
    

​ export default function counter(state = 0, action) {

​ switch (action.type) {

​ case 'INCREMENT':

​ return state + action.data

​ case 'DECREMENT':

​ return state - action.data

​ default:

​ return state

​ }

​ }

  1. 注意
    

a. 返回一个新的状态

b. 不要修改原来的状态

# 8.3.3. store

  1. 将state,action与reducer联系在一起的对象
    
  2. 如何得到此对象?
    

​ import {createStore} from 'redux'

​ import reducer from './reducers'

​ const store = createStore(reducer)

  1. 此对象的功能?
    

​ getState(): 得到state

​ dispatch(action): 分发action, 触发reducer调用, 产生新的state

​ subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

# 8.4. 使用redux编写应用

# 8.4.1. 效果

# 8.4.2. 下载依赖包

npm install --save redux

# 8.4.3. redux/action_types.js

*/\* *  *a**ction**对象的**type**常量名称模块**  \*/ *  **export const** INCREMENT = **'increment' **  **export const** DECREMENT = **'decrement'**
1

# 8.4.4. redux/actions_creators.js

*/\*  action creator**模块**  \*/ *  **import** {INCREMENT, DECREMENT} **from** **'./action-types'    **  **export const** *increment* = number => ({**type**: INCREMENT, number})  **export const** *decrement* = number => ({**type**: DECREMENT, number})
1

# 8.4.5. redux/reducers.js

*/\* *  *根据老的**state**和指定**action,* *处理返回一个新的**state  \*/ *  **import** {INCREMENT, DECREMENT} **from** **'../constants/ActionTypes'    **  **import** {INCREMENT, DECREMENT} **from** **'./action-types'    **  **export function** *counter*(state = 0, action) {  **console**.log(**'counter'**, state, action)  **switch** (action.**type**) {   **case** INCREMENT:    **return** state + action.**number **   **case** DECREMENT:    **return** state - action.**number **   **default**:    **return** state  } }
1

# 8.4.6. components/app.jsx

*/\* *  *应用组件**  \*/ *  **import** React, {Component} **from** **'react' **  **import** PropTypes **from** **'prop-types' **  **import** * **as** actions **from** **'../redux/actions'    **  **export default class** App **extends** Component {    **static** *propTypes* = {   **store**: PropTypes.*object*.isRequired,  }    *increment* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1   **this**.**props**.**store**.dispatch(actions.*increment*(number))  }    *decrement* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1   **this**.**props**.**store**.dispatch(actions.*decrement*(number))  }    *incrementIfOdd* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1     **let** count = **this**.**props**.**store**.getState()   **if** (count % 2 === 1) {    **this**.**props**.**store**.dispatch(actions.*increment*(number))   }  }    *incrementAsync* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1   setTimeout(() => {    **this**.**props**.**store**.dispatch(actions.*increment*(number))   }, 1000)  }    render() {   **return** (    <**div**>     <**p**>      click {**this**.**props**.**store**.getState()} times {**' '**}     </**p**>     <**select** **ref****="numSelect"**>      <**option** **value****="1"**>1</**option**>      <**option** **value****="2"**>2</**option**>      <**option** **value****="3"**>3</**option**>     </**select**>{**' '**}     <**button** **onClick****=**{**this**.*increment*}>+</**button**>     {**' '**}     <**button** **onClick****=**{**this**.*decrement*}>-</**button**>     {**' '**}     <**button** **onClick****=**{**this**.*incrementIfOdd*}>increment if odd</**button**>     {**' '**}     <**button** **onClick****=**{**this**.*incrementAsync*}>increment async</**button**>    </**div**>   )  } }`` 
1

# 8.4.7. store.js

**import** React **from** **'react' **  **import** ReactDOM **from** **'react-dom' **  **import** {createStore} **from** **'redux'    **  **import** App **from** **'./components/app' **  **import** {*counter*} **from** **'./redux/reducers'    **  *//* *根据**counter**函数创建**store**对象 *  **const** store = createStore(*counter*)    *//* *定义渲染根组件标签的函数 *  **const** *render* = () => {  ReactDOM.render(   <**App** **store****=**{store}/>,   **document**.getElementById(**'root'**)  ) }  *//* *初始化渲染 *  *render*()    *//* *注册**(**订阅**)**监听**,* *一旦状态发生改变**,* *自动重新渲染 *  store.subscribe(*render*)
1

# 8.4.8. 问题

  1. redux与react组件的代码耦合度太高
    
  2. 编码不够简洁
    

# 8.5. react-redux

# 8.5.1. 理解

  1. 一个react插件库
    
  2. 专门用来简化react应用中使用redux
    

# 8.5.2. React-Redux将所有组件分成两大类

  1. UI组件
    

a. 只负责 UI 的呈现,不带有任何业务逻辑

b. 通过props接收数据(一般数据和函数)

c. 不使用任何 Redux 的 API

d. 一般保存在components文件夹下

  1. 容器组件
    

a. 负责管理数据和业务逻辑,不负责UI的呈现

b. 使用 Redux 的 API

c. 一般保存在containers文件夹下

# 8.5.3. 相关API

  1. Provider
    

让所有组件都可以得到state数据

  1. connect()
    

用于包装 UI 组件生成容器组件

import { connect } from 'react-redux' connect( mapStateToprops, mapDispatchToProps )(Counter)

  1. mapStateToprops()
    

将外部的数据(即state对象)转换为UI组件的标签属性 const mapStateToprops = function (state) { return { value: state } }

  1. mapDispatchToProps()
    

将分发action的函数转换为UI组件的标签属性

简洁语法可以直接指定为actions对象或包含多个action方法的对象

# 8.5.4. 使用react-redux

  1. 下载依赖包
    

npm install --save react-redux

  1. redux/action_types.js
    

不变

  1. redux/actions_creators.js
    

不变

  1. redux/reducers.js
    

不变

  1. components/counter.jsx
    
*/\*  UI**组件**:* *不包含任何**redux API  \*/ *  **import** React **from** **'react' **  **import** PropTypes **from** **'prop-types'    **  **export default class** Counter **extends** React.Component {    **static** *propTypes* = {   **count**: PropTypes.*number*.isRequired,   **increment**: PropTypes.*func*.isRequired,   **decrement**: PropTypes.*func*.isRequired  }    *increment* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1   **this**.**props**.**increment**(number)  }    *decrement* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1   **this**.**props**.**decrement**(number)  }    *incrementIfOdd* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1   **let** count = **this**.**props**.**count **   **if** (count % 2 === 1) {    **this**.**props**.**increment**(number)   }  }    *incrementAsync* = () => {   **const** number = **this**.**refs**.numSelect.**value** * 1   setTimeout(() => {    **this**.**props**.**increment**(number)   }, 1000)  }    render() {   **return** (    <**div**>     <**p**>      click {**this**.**props**.**count**} times {**' '**}     </**p**>     <**select** **ref****="numSelect"**>      <**option** **value****="1"**>1</**option**>      <**option** **value****="2"**>2</**option**>      <**option** **value****="3"**>3</**option**>     </**select**>{**' '**}     <**button** **onClick****=**{**this**.*increment*}>+</**button**>     {**' '**}     <**button** **onClick****=**{**this**.*decrement*}>-</**button**>     {**' '**}     <**button** **onClick****=**{**this**.*incrementIfOdd*}>increment if odd</**button**>     {**' '**}     <**button** **onClick****=**{**this**.*incrementAsync*}>increment async</**button**>    </**div**>   )  } }
1
  1. containters/App.jsx
    
*/\* *  *包含**Counter**组件的容器组件**  \*/ *  **import** React **from** **'react' **  *//* *引入连接函数 *  **import** {connect} **from** **'react-redux' **  *//* *引入**action**函数 *  **import** {*increment*, *decrement*} **from** **'../redux/actions'    **  **import** Counter **from** **'../components/counter'    **  *//* *向外暴露连接**App**组件的包装组件 *  **export default** connect(  state => ({**count**: state}),  {*increment*, *decrement*} )(Counter)
1
  1. index.js
    
**import** React **from** **'react' **  **import** ReactDOM **from** **'react-dom' **  **import** {createStore} **from** **'redux' **  **import** {Provider} **from** **'react-redux'    **  **import** App **from** **'./****container****s/app' **  **import** {*counter*} **from** **'./redux/reducers'    **  *//* *根据**counter**函数创建**store**对象 *  **const** store = createStore(*counter*)    *//* *定义渲染根组件标签的函数 *  ReactDOM.render(  (   <**Provider** **store****=**{store}>    <**App** />   </**Provider**>  ),  **document**.getElementById(**'root'**)`` 
1

# 8.5.5. 问题

  1. redux默认是不能进行异步处理的,

  2. 应用中又需要在redux中执行异步任务(ajax, 定时器)

# 8.6. redux异步编程

# 8.6.1. 下载redux插件(异步中间件)

npm install --save redux-thunk

# 8.6.2. store.js

**import** {createStore, *applyMiddleware*} **from** **'redux' **  **import** thunk **from 'redux-thunk'**** **  *//* *根据**reducer**函数创建**store**对象 *  **const** store = createStore(  *reducer*,  *applyMiddleware(thunk)* *//* *应用上异步中间件 *  )
1

# 8.6.3. redux/actions.js

*//* *异步**action creator(**返回一个函数**) *  **export const** *incrementAsync* = number => {  **return** dispatch => {   setTimeout(() => {    dispatch(*increment*(number))   }, 1000)  } }
1

# 8.6.4. components/counter.jsx

*incrementAsync* = () => {  **const** number = **this**.**refs**.numSelect.**value***1  **this**.**props**.**incrementAsync**(number) }
1

# 8.6.5. containers/app.jsx

**import** {*increment*, *decrement*, *incrementAsync*} **from** **'../redux/actions'** // *向外暴露连接App组件的包装组件 * export default connect( state => ({count: state}), {increment, decrement, incrementAsync} )(Counter)

# 8.7. 使用上redux调试工具

# 8.7.1. 安装chrome浏览器插件

# 8.7.2. 下载工具依赖包

npm install --save-dev redux-devtools-extension
1

# 8.7.3. 编码

Store.js

**import** { **composeWithDevTools** } **from** **'redux-devtools-extension'**``* * **const** store = createStore( *reducer*, **composeWithDevTools**(*applyMiddleware*(thunk)) * * )

# 8.8. 相关重要知识: 纯函数和高阶函数

# 8.8.1. 纯函数

  1. 一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)

  2. 必须遵守以下一些约束

a. 不得改写参数数据

b. 不会产生任何副作用,例如网络请求,输入和输出设备

c. 不能调用Date.now()或者Math.random()等不纯的方法

  1. redux的reducer函数必须是一个纯函数

# 8.8.2. 高阶函数

  1. 理解: 一类特别的函数
    

a. 情况1: 参数是函数

b. 情况2: 返回是函数

  1.     常见的高阶函数: 
    

a. 定时器设置函数

b. 数组的forEach()/map()/filter()/reduce()/find()

c. 函数对象的bind()

d. Promise() / then()

e. antd中的Form.create()()

f. react-router-dom中的withRouter

g. react-redux中的connect()

  1.     作用: 
    

a. 能实现更加动态, 更加可扩展的功能

编辑 (opens new window)
上次更新: 2021/07/01, 20:33:02
webpack-yarn环境搭建
vue

← webpack-yarn环境搭建 vue→

最近更新
01
数组常用方法
05-21
02
边框
04-30
03
js1
04-29
更多文章>
Theme by Vdoing | Copyright © 2021-2021 本博客仅仅作为自己日常学习记录笔记使用
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×