# base-react
**Repository Path**: lisa_zhu2012/base-react
## Basic Information
- **Project Name**: base-react
- **Description**: react 基础学习
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-12-22
- **Last Updated**: 2022-07-01
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# React
> 快捷键
```
rcc 类组件
rfc 函数式组件
imr 引入react
af () => {}
```
# 第1章:React入门
## 介绍
> 1. 用于动态构建用户界面的JavaScript库(只注重于视图)
> 2. 由Facebook开源
### 特点
1. 采用**组件化**模式、**声明式编码**,提高开发效率及组件复用率。
2. 在React Native中可以使用React语法进行**移动端开发**。
3. 使用**虚拟DOM**+优秀的**Diffing算法**,尽量减少与真实DOM的交互。
### React高效的原因
1. 使用虚拟(virtual)DOM,不总是直接操作页面真实DOM
2. DOM Diffing算法,最小化页面重绘
## React的基本使用
> 学习React之前你要掌握的js基础知识:判断this的指向,class(类),es6语法规范,npm包管理器,原型、原型链,数组常用方法,模块化
### 相关js库
1. react.js:React核心库
2. react-dom.js:提供操作DOM的react扩展库
3. babel.min.js:解析JSX语法代码转为JS代码的库
### 创建虚拟DOM的两种方式
1. 纯JS方式(一般不用)
```bash
# 1. 创建虚拟DOM
# const VDOM = React.createElement('h1', {id: 'title'}, 'Hello,React')
const VDOM = React.createElement('h1', {id: 'title'}, React.createElement('span', null, 'Hello,React'))
# 2. 渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
```
2. JSX方式
关于虚拟DOM:
1. 本质是Object类型的对象(一般对象)
2. 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
3. 虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
## React JSX
### JSX
1. 全称JavaScript XML
2. **react定义的一种类似于XML的JS扩展语法:JS+XML**
> XML早起用与存储和传输数据Tom19 => json
3. 本质是React.createElement(component, props, ...children)方法的语法糖
4. 作用:用来简化创建虚拟DOM
- 写法:var ele =
Hello JSX!
- 注意1:它不是字符串,也不是HTML/XML标签
- 注意2:它最终产生的就是一个JS对象
5. 标签名任意:HTML标签或其他标签
6. 基本语法规则
- 遇到 < 开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
- 遇到以 { 开头的代码,以JS语法解析: 标签中的js表达式必须用{ }包含
7. babel.js的作用
- 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
- 只要用了JSX,都要加上type="text/babel", 声明需要babel来处理
jsx语法规则:
1. 定义虚拟DOM时,不要写引号;
2. 标签中混入JS表达式时要用{};
3. 样式的类名指定不要用class,要用className;
4. 内联样式:要用style={{key:value}}的形式去写;
5. 只有一个根标签;
6. 标签必须闭合;
7. 标签首字母:
- 若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错;
- 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错;
一定注意区分:【js语句(代码)】与【js表达式】
1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值得地方
下面这些都是表达式:
- a (读出一个值)
- a+b
- demo(1)
- arr.map()
- function test() {}
2. 语句(代码):
下面这些都是语句(代码)(控制代码走向)
- if(){}
- for(){}
- switch(){case:xxxx}
### 渲染虚拟DOM(元素)
1. 语法: ReactDOM.render(virtualDOM, containerDOM)
2. 作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示
3. 参数说明
- 参数一: 纯js或jsx创建的虚拟dom对象
- 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
## 模块与组件和模块化与组件化的理解
### 模块
1. 理解:向外提供特定功能的js程序,一般就是一个js文件
2. 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。
3. 作用:复用js,简化js的编写,提高js运行效率
### 组件
1. 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
2. 为什么要用组件: 一个界面的功能更复杂
3. 作用:复用编码, 简化项目编码, 提高运行效率
### 模块化
当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
### 组件化
当应用是以多组件的方式实现, 这个应用就是一个组件化的应用
# 第2章:React面向组件编程
## react中定义组件
函数式组件:用函数定义的组件(适用于【简单组件】的定义)
类式组件:用类定义的组件(适用于【复杂组件】的定义)
类的基本知识:
1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
3. 类中所定义的方法,都是放在了类的原型对象上,供实例去使用。
人 状态 影响 行为
组件 状态 驱动 页面
### 组件实例的三大核心属性 state
1. state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
2. 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
注意:
* 组件中render方法中的this为组件实例对象
* 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this:通过函数对象的bind()
- 箭头函数:要用赋值语句的形式+箭头函数
* 状态数据,不能直接修改或更新
### 组件实例的三大核心属性 props
1. 每个组件对象都会有props(properties的简写)属性
2. 组件标签的所有属性都保存在props中
作用:
1. 通过标签属性从组件外向组件内传递变化的数据
2. 注意:组件内部不要修改props数据
编码操作:
1. 内部读取某个属性值```this.props.name```
2. 对props中的属性值进行类型限制和必要性限制
第一种方式(React v15.5开始已启用):
```Person.propTypes = {
name: React.PropTypes.sting.isRequired,
age: React.PropTypes.number
}
```
第二种方式(新):
使用prop-types库进行限制(需要引入prop-types库)
```
Person.propTypes = {
name: PropTypes.sting.isRequired,
age: PropTypes.number
}
```
3. 扩展属性:将对象的所有属性通过props传递
```
```
4. 默认属性值
```
Person.defaultProps = {
age: 18,
sex: '男'
}
```
5. 组件类的构造函数
> 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
```
constructor(props) {
super(props)
console.log(props) // 打印所有属性
}
```
### 组件实例的三大核心属性 refs与事件处理
组件内的标签可以定义ref属性来标识自己
编码:
1. 字符串形式的ref
``````
2. 回调函数形式的ref
```
{this.input1 = c}}/>
```
3. createRef创建ref容器
```myRef = React.createRef()
this.myRef.current.value
```
React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
## 事件处理
1. 通过onXxx属性指定事件处理函数(注意大小写)
- React使用的是自定义(合成)事件,而不是使用的原生DOM事件 —— 为了更好的兼容性
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) —— 为了高效
2. 通过event.target得到发生事件的DOM元素对象 —— 勿过度使用ref
## 收集表单数据
包含表单的组件分类
* 受控组件
* 非受控组件
* 高阶函数:如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数。
1. 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
* 函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
```
function sum(a) {
return (b) => {
return (c) => {
return a + b + c
}
}
}
const result = sum(1)(2)(3)
console.log(result)
```
## 组件生命周期
### 生命周期
1. 组件从创建到死亡它会经历一些特定的阶段;
2. React组件中包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用;
3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
* 生命周期的三个阶段(旧)
1. 初始化阶段:由ReactDOM.render()触发---初次渲染
constructor()
componentWillMount()
render()
componentDidMount() ==> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段:由组件内部this.setState()或父组件重新render触发
shouldComponentUpdate()
componentWillUpdate()
render() ==> 必须使用的一个
componentDidUpdata()
3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount() ==> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消消息
* 生命周期的三个阶段(新)
1. 初始化阶段:由ReactDOM.render()触发---初次渲染
constructor()
getDerivedStateFromProps
render()
componentDidMount() ==> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段:由组件内部this.setState()或父组件重新render触发
getDerivedStateFromProps
shouldComponentUpdate()
render() ==> 必须使用的一个
getSnapshotBeforeUpdate
componentDidUpdata()
3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount() ==> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消消息
### 重要的钩子
1. render 初始化渲染或更新渲染调用
2. componentDidMount 开启监听,发送ajax请求
3. componentWillUnmount 做一些收尾工作,如:清理定时器
### 即将废弃的钩子
componentWillMount
componentWillReceiveProps
componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAEF_前缀才能使用,以后可能会被彻底废弃,不建议使用
## 虚拟DOM与DOM Diffing算法
经典面试题:
1). react/vue中的key有什么作用?(key的内部原理是什么?)
2). 为什么遍历列表时,key最好不要用index?
1. 虚拟DOM中key的作用:
1).简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用。
2).详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a.旧虚拟DOM中找到了与新虚拟DOM相同的key;
(1).若虚拟DOM中内容没变,直接使用之前的真实DOM
(2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b.旧虚拟DOM中未找到与新虚拟DOM相同的key:
根据数据创建新的真实DOM,随后渲染到页面
2. 用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低。
2. 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题
3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
3. 开发中如何选择key?
1. 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
2. 如果确定只是简单的展示数据,用index也是可以的
# 第3章:react应用(基于react脚手架)
## 使用create-react-app创建react应用
> Yarn Facebook 贡献的 Javascript 包管理器
### react脚手架
1. xxx脚手架:用来帮助程序员快速创建一个基于xxx库的**模板项目**
* 包含了所有需要的配置(语法检查、jsx编译、devServer...)
* 下载好了所有相关的依赖
* 可以直接运行一个简单效果
2. react提供了一个用于创建react项目的脚手架库:create-react-app
3. 项目的整体技术架构为:react + webpack + es6 + eslint
4. 使用脚手架开发的项目的特点:模块化,组件化,工程化
### 创建项目并启动
```bash
# 全局安装
npm install -g create-react-app
# 切换到想创建项目的目录,使用命令
create-react-app hello-react
# 进入项目文件夹
cd hello-react
# 启动项目
npm start
# 查看全局安装的库
npm list -g --dept 0
# 强制清理缓存
npm cache clean --force
```
### 功能界面的组件化编码流程(通用)
1. 拆分组件:拆分界面,抽取组件
2. 实现静态组件:使用组件实现静态页面效果
3. 实现动态组件
动态显示初始化数据
数据类型,数据名称,保存在哪个组件
交互(从绑定事件监听开始)
#### todoList案例相关知识点
1. 拆分组件、实现静态组件;注意:className、style的写法
2. 动态初始化列表,如何确定将数据放在哪个组件的state中?
- 某个组件使用:放在其自身的state中
- 某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
3. 关于父子之间通信:
- 【父组件】给【子组件】传递数据:通过props传递
- 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
4. 注意:defaultChecked和checked的区别,类似的还有:defaultValue和value
5. **状态在哪里,操作状态的方法就在哪里**
nanoid prop-types
# 第4章:react ajax
## 理解
### 前置说明
1. React本身只关注于界面,并不包含发送ajax请求的代码
2. 前端应用需要通过ajax请求与后台进行交互(json数据)
3. react应用中需要继承第三方ajax库(或自己封装)
### 常用的ajax请求库
* jQuery 比较重,如果需要另外引入不建议使用
* axios 轻量级,建议使用
- 封装XMLHttpRequest对象的ajax
- promise风格
- 可以用在浏览器端和node服务器端
```
var Ajax = {
get: function(url, fn) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.onreadystatechange = function() {
// readyState == 4说明请求已完成
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304) {
// console.log(xhr.responseText);
// fn.call(xhr.responseText);
}
}
}
xhr.send();
},
post: function(url, data, fn) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, false);
// 添加http头,发送信息至服务器时内容编码类型
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304) {
// console.log(xhr.responseText);
// fn.call(xhr.responseText);
}
}
}
xhr.send(data);
}
}
function ajax(url,funWin,funFaild){ //回调函数
//1.创建ajax对象
var xhr= window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject(“Microsoft.XMLHTTP”);
//2.与服务器建立连接
xhr.open(“GET”,url,true);
//3.发送请求
xhr.send();
//4.接收返回
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){ //请求状态
if(xhr.status === 200){
if(typeof funWin === "function"){
funWin(xhr.responseText);
}
} else {
if(typeof funFaild === "function"){
funFaild();
}
}
}
}
}
```
## axios
### react脚手架配置代理总结
#### 方法一
> 在package.json中追加如下配置
```json
"proxy":"http://localhost:5000"
```
说明:
1. 优点:配置简单,前端请求资源时可以不加任何前缀。
2. 缺点:不能配置多个代理。
3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
#### 方法二
1. 第一步:创建代理配置文件
```
在src下创建配置文件:src/setupProxy.js
```
2. 编写setupProxy.js配置具体代理规则:
```js
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
proxy('/api1', { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
changeOrigin: true, //控制服务器接收到的请求头中host字段的值
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
changeOrigin默认值为false,但我们一般将changeOrigin值设为true
*/
pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
}),
proxy('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: {'^/api2': ''}
})
)
}
```
说明:
1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
2. 缺点:配置繁琐,前端请求资源时必须加前缀。
### 消息订阅-发布机制
```bash
# 引入
import PubSub from 'pubsub-js'
# 订阅
PubSub.subscribe('delete', function(msg, data){ });
# 发布消息
PubSub.publish('delete', data)
```
### 扩展:[fetch](https://github.github.io/fetch/)
> https://segmentfault.com/a/1190000003810652
* 特点
- fetch原生函数,不再使用XMLHttpRequest对象提交ajax请求
- 老版本浏览器可能不支持
* GET请求
```
fetch(url).then(function(response) {
return response.json()
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
});
```
* POST请求
```
fetch(url, {
method: "POST",
body: JSON.stringify(data)
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
})
```
#### github搜索案例相关知识点
1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
2. ES6小知识点:解构赋值+重命名
```
let obj = {a:{b:1}}
const {a} = obj; // 传统解构赋值
const {a:{b}} = obj; // 连续解构赋值
const {a:{b:value}} = obj; // 连续解构赋值 + 重命名
```
3. 消息订阅与发布机制
* 先订阅,再发布(理解:有一种隔空对话的感觉)
* 适用于任意组件间通信
* 要在组件的componentWillUnmount中取消订阅
4. fetch发送请求(关注分离的设计思想)
```
try {
const response = await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
console.log(data)
} catch (error) {
console.log('请求出错', error)
}
```
# 第5章:react-router
## 相关理解
### SPA的理解
1. 单页Web应用(single page web application, SPA)
> 单页面多组件
2. 整个应用只有一个完整的页面
3. 点击页面中的点解不会刷新页面,只会做页面的局部更新
4. 数据都需要通过ajax请求获取,并在前端异步展现
### 路由的理解
#### 什么是路由?
- 一个路由就是一个映射关系(key:value)
- key为路径, value可能是function或component
#### 路由分类
1. 后端路由:
1)理解:value是function, 用来处理客户端提交的请求。
2)注册路由:router.get(path, function(req, res))
3)工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
2. 前端路由:
1)浏览器端路由:value是component,用于展示页面内容。
2)注册路由:
3)工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
### react-router-dom的理解
> web native any
1. react的一个插件库
2. 专门用来实现一个SPA应用
3. 基于react的项目基本都会用到此库
## react-router相关API
### 路由的基本使用
1. 明确好界面中的导航区、展示区
2. 导航区的a连接改为Link标签
Demo
3. 展示区写Route标签进行路径的匹配
4. 最外侧包裹了一个或
### 路由组件与一般组件
1. 写法不同:
一般组件:
路由组件:
2. 存放位置不同:
一般组件:components
路由组件:pages
3. **接收到的props不同**:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
```
history:
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
location: {pathname: "/about", search: "", state: undefined}
push: ƒ push(path, state)
replace: ƒ replace(path, state)
location:
pathname: "/about"
search: ""
state: undefined
match:
isExact: true
params: {}
path: "/about"
url: "/about"
```
### NavLink与封装NavLink
1. NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
2. 标签体内容是一个特殊的标签属性
3. 通过this.props.children可以获取标签体内容
### Switch
1. 通常情况下,path和component是一一对应的关系。
2. Switch可以提高路由匹配效率(单一匹配)。
### 解决多级路径刷新页面样式丢失的问题
1. public/index.html 中引入样式时不写 ./ 写 /(常用)
2. public/index.html 中引入样式时不写 ./ 些 %PUBLIC_URL%(常用)
3. 使用HashRouter
### 路由的严格匹配与模糊匹配
1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
2. 开启严格匹配:
3. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
### Redirect的使用
1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
2. 具体编码:
```
```
### 嵌套路由
1. 注册子路由时要写上父路由的path值
2. 路由的匹配是按照注册路由的顺序进行的
### 向路由组件传递参数
> ajax: query、params、body{urlencode json}
1. params参数
路由链接(携带参数):详情
注册路由(声明接收):
接收参数:this.props.match.params
2. search参数
路由链接(携带参数):详情
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3. state参数
路由链接(携带参数):``` 详情 ```
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.state
备注:刷新也可以保留住参数(清除缓存单独访问url不能保留参数)
### 编程式路由导航
借助this.props.history对象上的API对操作路由跳转、前进、后退
this.props.history.push()/replace()/goBack()/goForward()/go()
### withRouter函数
withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
withRouter的返回值是一个新组件
### BrowserRouter与HashRouter的区别
1. 底层原理不一样
BrowserRouter:使用的是H5的history API,不兼容IE9及以下版本。
HashRouter:使用的是URL的哈希值。
2. path表现形式不一样
BrowserRouter:路径中没有#,例如:localhost:3000/demo/test
HashRouter:路径包含#,例如:localhsot:3000/#/demo/test
3. 刷新后对路由state参数的影响
BrowserRouter:没有任何影响,因为state保存在history对象中
HashRouter:刷新后会导致路由state参数的丢失
4. 备注:HashRouter可以用于解决一些路径错误相关的问题(ps:路径丢失问题)。
### 内置组件
### 其他
history对象
mathch对象
withRouter函数
# 第6章:react UI组件库
流行的开源React UI组件库
[material-ui(国外)](http://www.material-ui.com)
[ant-design(国内蚂蚁金服)](https://ant.design/index-cn)
## antd的按需引入+自定义主题
1. 安装依赖:
```
yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
```
2. 修改package.json
```
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
```
3. 根目录下创建config-overrides.js
```
//配置具体的修改规则
const { override, fixBabelImports,addLessLoader} = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
}),
addLessLoader({
lessOptions:{
javascriptEnabled: true,
modifyVars: { '@primary-color': 'green' },
}
}),
);
```
4. 备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css’应该删掉
# 第7章:redux
## redux理解
### 学习文档
1. 英文文档: https://redux.js.org/
2. 中文文档: http://www.redux.org.cn/
3. Github: https://github.com/reactjs/redux
### redux是什么
1. redux是一个专门用于做状态管理的JS库(不是react插件库)。
2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
3. 作用: 集中式管理react应用中多个组件共享的状态。
### 什么情况下需要使用redux
1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。
2. 一个组件需要改变另一个组件的状态(通信)。
3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
### redux工作流程
原理图 \base-react\原理图\redux原理图.png
### redux的三个核心概念
#### action
1. 动作的对象
2. 包含2个属性
* type:标识属性,值为字符串,唯一,必要属性
* data:数据属性,值类型任意,可选属性
3. 例子{type: ADD_STUDENT, data: {name: 'tom', age: 18}}
#### reducer
1. 用于初始化状态,加工状态
2. 加工时,根据旧的state和action,产生新的state的纯函数。
#### store
1. 将state、action、reducer联系在一起的对象
2. 如何得到此对象?
```
import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
```
3. 此对象的功能?
* getState():得到state
* dispatch(action):分发action,触发reducer调用,产生新的state
* subscribe(listener):注册监听,当产生了新的state时,自动调用
### redux的核心API
#### createstore()
作用:创建包含指定reducer的store对象
#### store对象
1. 作用:redux库最核心的管理对象
2. 它内部维护着:state reducer
3. 核心方法:getState() dispatch(action) subscribe(listener)
4. 具体编码:store.getState() store.dispatch({type:'INCREMENT',number}) store.subscribe(render)
#### applyMiddleware()
作用:应用上基于redux的中间件(插件库)
#### combineReducers()
作用:合并多个reducer函数
### redux异步编程
1. 理解:
redux默认是不能进行异步处理的;某些时候应用中需要在redux中执行异步任务(ajax,定时器)
2. 使用异步中间件
redux-thunk
### react-redux
1. 理解:
* 一个react插件库
* 专门用来简化react应用中使用redux
2. react-Redux将所有组件分成两大类
* UI组件
1)只负责 UI 的呈现,不带有任何业务逻辑
2)通过props接收数据(一般数据和函数)
3)不使用任何 Redux 的 API
4)一般保存在components文件夹下
* 容器组件
1)负责管理数据和业务逻辑,不负责UI的呈现
2)使用 Redux 的 API
3)一般保存在containers文件夹下
3. 相关API
* Provider:让所有组件都可以得到state数据
```
```
* connect:用于包装 UI 组件生成容器组件
```
import { connect } from 'react-redux'
connect(
mapStateToprops,
mapDispatchToProps
)(Counter)
```
* mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
```
const mapStateToprops = function (state) {
return {
value: state
}
}
```
* mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
### 纯函数和高阶函数
* 纯函数
1. 一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)
2. 必定遵守以下一些约束
- 不得改写参数数据
- 不会产生任何副作用,例如网络请求,输入和输出设备
- 不能调用Date.now()或者Math.random()等不纯的方法
3. redux的reducer函数必须是一个纯函数
* 高阶函数
1. 理解:一类特别的函数
- 情况1:参数是函数
- 情况2:返回是参数
2. 常见的高阶函数:
- 定时器设置函数
- 数组的forEach()/map()/filter()/reduce()/find()/bind()
- promise
- react-redux中的connect函数
3. 作用:能实现更加动态,更加可扩展的功能
const startTime = performance.now();
something();
const endTime = performance.now();
console.log(`Cpde take ${startTime - endTime} milliseconds`)