redux
一、学习文档
-
英文文档: redux.js.org/
-
中文文档: www.redux.org.cn/
-
Github: github.com/reactjs/red…
-
yarn add redux
二、 redux是什么
Redux就是一个帮助我们管理State的容器:Redux是JavaScript的状态容器,提供了可预测的状态管理
1.redux是一个专门用于做状态管理的JS库(不是react插件库)。
2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
3. 作用: 集中式管理react应用中多个组件共享的状态。
三、 什么情况使用 Redux ?
首先,我们先明晰 Redux 的作用 ,实现集中式状态管理。
Redux 适用于多交互、多数据源的场景。简单理解就是复杂
从组件角度去考虑的话,当我们有以下的应用场景时,我们可以尝试采用 Redux 来实现
- 某个组件的状态需要共享时
- 一个组件需要改变其他组件的状态时
- 一个组件需要改变全局的状态时
除此之外,还有很多情况都需要使用 Redux 来实现(还没有学 hook,或许还有更好的方法)
这张图,非常形象的将纯 React 和 采用 Redux 的区别体现了出来
四、 Redux 的工作流程
首先组件会在 Redux 中派发一个 action 方法,通过调用 store.dispatch 方法,将 action 对象派发给 store ,当 store 接收到 action 对象时,会将先前的 state 与传来的 action 一同发送给 reducer ,reducer 在接收到数据后,进行数据的更改,返回一个新的状态给 store ,最后由 store 更改 state
五、 redux的三个核心概念
1. action
动作的对象 包含 2 个属性
- type:标识属性, 值为字符串, 唯一, 必要属性
- data:数据属性, 值类型任意, 可选属性
例子:
{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }
2. reducer
- 用于初始化状态、加工状态。
- 加工时,根据旧的state和action, 产生新的state的纯函数。
- 初始化时,传入的preState为undefined
3. store
将state、action、reducer联系在一起的对象
如何得到此对象?
store.js
/*
该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)
此对象的功能?
- getState(): 得到state
- dispatch(action): 分发action, 触发reducer调用, 产生新的state
- subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
六、 redux的核心API
-
createStore()作用:创建包含指定reducer的store对象 -
store对象作用: redux库最核心的管理对象
它内部维护着:
- state
- reducer
核心方法:
getState()
dispatch(action)
subscribe(listener)
具体编码:
store.getState()
store.dispatch({type:'INCREMENT', number})
store.subscribe(render)
-
applyMiddleware()作用:应用上基于redux的中间件(插件库) -
combineReducers()作用:合并多个reducer函数
案例
constants.js
export const ADD_NUMBER = "ADD_NUMBER";
export const SUB_NUMBER = "SUB_NUMBER";
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";
actionCreators.js
import {
ADD_NUMBER,
SUB_NUMBER,
INCREMENT,
DECREMENT
} from './constants.js';
// export function addAction(num) {
// return {
// type: "ADD_NUMBER",
// num
// }
// }
// export const addAction = (num) => {
// return {
// type: "ADD_NUMBER",
// num
// }
// }
//返回一个对象可以使用简写
export const addAction = num => ({
type: ADD_NUMBER,
num
});
export const subAction = num => ({
type: SUB_NUMBER,
num
});
export const incAction = () => ({
type: INCREMENT
});
export const decAction = () => ({
type: DECREMENT
});
reducer.js
import {
ADD_NUMBER,
SUB_NUMBER,
INCREMENT,
DECREMENT
} from './constants.js';
const defaultState = {
counter: 0
}
function reducer(state = defaultState, action) {
switch (action.type) {
case ADD_NUMBER:
return { ...state, counter: state.counter + action.num };
case SUB_NUMBER:
return { ...state, counter: state.counter - action.num };
case INCREMENT:
return { ...state, counter: state.counter + 1 };
case DECREMENT:
return { ...state, counter: state.counter - 1 };
default:
return state;
}
}
export default reducer;
index.js/store
import {createStore} from 'redux'
import reducer from './reducer.js';
const store = createStore(reducer);
export default store;
index.js
import store from './store/index.js';
import {
addAction,
subAction,
incAction,
decAction
} from './store/actionCreators.js';
store.subscribe(() => {
console.log(store.getState());
})
store.dispatch(addAction(10));
store.dispatch(addAction(15));
store.dispatch(subAction(8));
store.dispatch(subAction(5));
store.dispatch(incAction());
store.dispatch(decAction());
七、 redux异步编程
1. 理解
redux默认是不能进行异步处理的, 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
count_action.js
该模块专门为Count组件生成action对象
异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
2. 使用异步中间件
npm install redux-thunk
store.js
该模块专门用于暴露一个store对象,整个应用只有一个store对象
在这里引入中间件
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer,applyMiddleware(thunk))
import {INCREMENT,DECREMENT} from './constant'
//同步action,就是指action的值为Object类型的一般对象,注意箭头函数简写时返回对象要带()
export const createIncrementAction = data => ({type:INCREMENT,data:data})
export const createDecrementAction = data => ({type:DECREMENT,data})
//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
export const createIncrementAsyncAction = (data,time) => {
return (dispatch)=>{
setTimeout(()=>{
dispatch(createIncrementAction(data))
},time)
}
}
3. 总结
- 明确:延迟的动作不想交给组件自身,想交给action
- 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
- 具体编码:
- npm install redux-thunk,并配置在store中
- 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
- 异步任务有结果后,分发一个同步的action去真正操作数据。
- 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
react-redux
一、 理解
一个React插件库 专门用来简化React应用中使用redux
二、 react-redux将所有组件分成两大类
1. UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过props接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在components文件夹下
2. 容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在containers文件夹下
三 、 模型图
四、 相关API
-
Provider:让所有组件都可以得到state数据
-
connect:用于包装 UI 组件生成容器组件
-
mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
-
mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
五、 基本使用
安装
npm install react-redux
yarn add react-redux
1. react-redux基本使用
(1).明确两个概念:
1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
2).容器组件:负责和redux通信,将结果交给UI组件。
(2).如何创建一个容器组件————靠react-redux 的 connect函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
-mapStateToProps:映射状态,返回值是一个对象
-mapDispatchToProps:映射操作状态的方法,返回值是一个对象
(3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
(4).备注2:mapDispatchToProps,也可以是一个对象
2. react-redux优化
(1).容器组件和UI组件整合一个文件
(2).无需自己给容器组件传递store,把所有容器都需要的store交给Provider,给<App/>包裹一个<Provider store={store}>即可。
(3).使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
(4).mapDispatchToProps也可以简单的写成一个对象,这样可以省略dispatch的调用
(5).一个组件要和redux“打交道”要经过哪几步?
(1).定义好UI组件---不暴露
(2).引入connect生成一个容器组件,并暴露,写法如下:
connect(
state => ({key:value}), //映射状态
{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
(3).在UI组件中通过this.props.xxxxxxx读取和操作状态
3. react-redux数据共享版
(1).定义一个Pserson组件,和Count组件通过redux共享数据。
(2).为Person组件编写:reducer、action,配置constant常量。
(3).重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,
合并后的总状态是一个对象!!!
(4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
六、 纯函数和高阶函数
reducer要求是一个纯函数,所以操作数组的时候,如果用push之类的方法,会改写参数数据,就不是纯函数了。
1. 纯函数
一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
不是说输入什么就输出什么,而是指不管调用多少次,只要输入的值相同,输出的值就相同 纯函数的维基百科定义:
在程序设计中,若一个函数符合一下条件,那么这个函数被称为纯函数:
- 此函数在相同的输入值时,需产生相同的输出。函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的 外部输出无关。
- 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。 当然上面的定义会过于的晦涩,所以我简单总结一下:
- 确定的输入,一定会产生确定的输出;
- 函数在执行过程中,不能产生副作用
必须遵守以下一些约束:
- 不得改写参数数据
- 不会产生任何副作用,例如网络请求,输入和输出设备
- 不能调用Date.now()或者Math.random()等不纯的方法
- redux的reducer函数必须是一个纯函数 为什么纯函数在函数式编程中非常重要呢?
- 因为你可以安心的写和安心的用;
- 你在写的时候保证了函数的纯度,只是但是实现自己的业务逻辑即可,不需要关心传入的内容或者依赖其他的外部变量;
- 你在用的时候,你确定你的输入内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出;
- React中就要求我们无论是函数还是class声明一个组件,这个组件都必须像纯函数一样,保护它们的props不被修改:
纯函数分析:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// 是否是一个纯函数: 是纯函数
function sum(num1, num2) {
return num1 + num2;
}
sum(20, 30);
sum(20, 30);
sum(20, 30);
sum(20, 30);
sum(20, 30);
// add函数是否是一个纯函数: 不是一个纯函数
let foo = 10;
function add(num) {
return foo + num;
}
add(5); // 15
foo = 20;
add(5); // 25
// 能否将上面的函数改写成一个纯函数
// add2就是一个纯函数
const bar = 10;
function add2(num) {
return bar + num;
}
// bar = 11; //报错
// add3是一个纯函数吗? 不是一个纯函数
const baz = {
count: 10,
};
function add3(num) {
return baz.count + num;
}
baz.count = 20;
// printInfo是一个纯函数吗? 不是一个纯函数
function printInfo(info) {
info.name = "why";
console.log(info.name, info.age);
}
const obj = {
name: "zgc",
age: 18,
};
printInfo(info);
// 对象中修改数据
const info = {
name: "why",
age: 18,
};
//不是纯函数
info.name = "kobe";
//是纯函数
const newInfo = { ...info, name: "kobe" };
</script>
</body>
</html>
2. 高阶函数
理解: 一类特别的函数
- 情况1: 参数是函数
- 情况2: 返回是函数 常见的高阶函数:
- 定时器设置函数
- 数组的forEach()/map()/filter()/reduce()/find()/bind()
- promise
- react-redux中的connect函数 作用: 能实现更加动态, 更加可扩展的功能
七、 react-redux开发者工具的使用
安装该工具需要两步:
- 第一步:在对应的浏览器中安装相关的插件(比如Chrome浏览器扩展商店中搜索Redux DevTools即可,其他方法可以参考 GitHub);
- 第二步:在redux中继承devtools的中间件; GitHub:github.com/zalmoxisus/…
方式一:
(1).yarn add redux-devtools-extension
(2).store中进行配置
//引入redux-devtools-extension,redux开发者工具
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
只会更改store.js中的文件
/*
该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware,combineReducers} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './reducers/count'
//引入为Person组件服务的reducer
import personReducer from './reducers/person'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//引入redux-devtools-extension开发者工具
import {composeWithDevTools} from 'redux-devtools-extension'
//汇总所有的reducer变为一个总的reducer
const allReducer = combineReducers({
he:countReducer,
rens:personReducer
})
//暴露store
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
方式二:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer.js";
// composeEnhancers函数
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) || compose;
const storeEnhancer = applyMiddleware(thunk);
const store = createStore(reducer, composeEnhancers(storeEnhancer));
export default store;
八、 react-redux最终版
(1).所有变量名字要规范,尽量触发对象的简写形式。
(2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
九、 组件中异步操作
- 在之前简单的案例中,redux中保存的state是一个本地定义的数据
- 我们可以直接通过同步的操作来dispatch action,state就会被立即更新。
- 但是真实开发中,redux中保存的很多数据可能来自服务器,我们需要进行异步的请求,再将数据保存到redux中。
- 在之前学习网络请求的时候我们讲过,网络请求可以在class组件的componentDidMount中发送,所以我们可以有这样的结构:
我现在完成如下案例操作:
- 在Home组件中请求banners和recommends的数据;
- 在About组件中展示banners和recommends的数据; 具体代码参考:codewhy--基础版示例
十、 redux中异步操作-thunk
上面的代码有一个缺陷:
- 我们必须将网络请求的异步代码放到组件的生命周期中来完成;
- 事实上,网络请求到的数据也属于我们状态管理的一部分,更好的一种方式应该是将其也交给redux来管理
但是在redux中如何可以进行异步的操作呢? 答案就是使用中间件(Middleware);
具体代码参考:codewhy--中间件--thunk
十、 lterator迭代器
1. 定义
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提 供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
-
ES6 创造了一种新的遍历命令
for...of循环,Iterator接口主要供for...of消费。 -
原生具备
iterator接口的数据(可用for of 遍历)ArrayArgumentsSetMapStringTypedArrayNodeList
案例:使用 next() 方法遍历原生自带 iterator 接口的数据:
// 遍历 Map
const mp = new Map();
mp.set('a', 1);
mp.set('b', 2);
mp.set('c', 3);
let iter1 = mp[Symbol.iterator]();
// next() 方法每执行一次,指针自增
console.log(iter1.next()); // { value: [ 'a', 1 ], done: false }
console.log(iter1.next()); // { value: [ 'b', 2 ], done: false }
console.log(iter1.next()); // { value: [ 'c', 3 ], done: false }
console.log(iter1.next()); // { value: undefined, done: true }
// 遍历数组
let xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
let iter2 = xiyou[Symbol.iterator]();
console.log(iter2.next()); // { value: '唐僧', done: false }
console.log(iter2.next()); // { value: '孙悟空', done: false }
console.log(iter2.next()); // { value: '猪八戒', done: false }
console.log(iter2.next()); // { value: '沙僧', done: false }
上面的案例只是为了证明他们自带 iterator 接口,实际上直接使用 for...of 方法遍历即可(iterator 接口为 for...of)服务。例如,可以使用 for [k, v] of map 来遍历 Map 数据结构中的键和值。
const mp = new Map();
mp.set('a', 1);
mp.set('b', 2);
mp.set('c', 3);
for (let [k, v] of mp) {
console.log(k, v);
}
/*
a 1
b 2
c 3
*/
2. 工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的
next方法,指针自动指向数据结构的第一个成员 - 接下来不断调用
next方法,指针一直往后移动,直到指向最后一个成员 - 每调用
next方法返回一个包含value和done属性的对象
应用场景:需要自定义遍历数据的时候,要想到迭代器。
3. 自定义遍历数据
我们可以通过给数据结构添加自定义 [Symbol.iterator]() 方法来使该数据结构能够直接被遍历,从而使 for...of 能够直接遍历指定数据,达到为 for...of 服务的功能。
// 需求:遍历对象中的数组
const banji = {
name : "终极一班",
stus: [
'aa',
'bb',
'cc',
'dd'
],
[Symbol.iterator](){
let index = 0;
let _this = this;
return {
next: () => {
if(index < this.stus.length){
const result = {value: _this.stus[index],done: false};
//下标自增
index++;
//返回结果
return result;
}else {
return {value: underfined,done:true};
}
}
}
}
}
// for...of直接遍历达到目的
for(let v of banji){
console.log(v); // aa bb cc dd
}
十一、 Generator生成器
ES6 新引入了
Generator函数,可以通过yield关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案,async就是Generator函数的语法糖。
*的位置没有限制- 使用
function * gen()和yield可以声明一个生成器函数。生成器函数返回的结果是迭代器对象. - 调用迭代器对象的
next方法可以得到yield语句后的值。 - 每一个
yield相当于函数的暂停标记,也可以认为是一个分隔符,每调用一次next(),生成器函数就往下执行一段。 next方法可以传递实参,作为上一个yield语句的返回值
1. Generator基础使用
1.1 Generator函数组成
Generator 有两个区分于普通函数的部分:
- 一是在 function 后面,函数名之前有个 * ;
- 函数内部有 yield 表达式。
其中 * 用来表示函数为 Generator 函数,yield 用来定义函数内部的状态。
function* func(args) {
console.log(args);
console.log("one");
yield "1";
console.log("two");
yield "2";
console.log("three");
return "3";
}
1.2 执行机制
调用 Generator 函数和调用普通函数一样,在函数名后面加上()即可,但是 Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。
var f = func('AAA');
console.log(f.next());
// AAA
// one
// {value: "1", done: false}
console.log(f.next());
// two
// {value: "2", done: false}
console.log(f.next());
// three
// {value: "3", done: true}
console.log(f.next());
// {value: undefined, done: true}
- 第一次调用 next 方法时,从 Generator 函数的头部开始执行,先是打印了 one ,执行到 yield 就停下来,并将yield 后边表达式的值 '1',作为返回对象的 value 属性值,此时函数还没有执行完, 返回对象的 done 属性值是 false。
- 第二次调用 next 方法时,同上步 。
- 第三次调用 next 方法时,先是打印了 three ,然后执行了函数的返回操作,并将 return 后面的表达式的值,作为返回对象的 value 属性值,此时函数已经结束,所以 done 属性值为true 。
- 第四次调用 next 方法时, 此时函数已经执行完了,所以返回 value 属性值是 undefined ,done 属性值是 true 。
- 如果执行第三步时,
没有 return 语句的话,就直接返回 {value: undefined, done: true}(return 方法提供参数时,返回该参数;不提供参数时,返回 undefined )。
function* foo() {
yield 1;
yield 2;
yield 3;
}
var f = foo();
console.log(f.next());
// {value: 1, done: false}
console.log(f.return("foo"));
// {value: "foo", done: true}
console.log(f.next());
// {value: undefined, done: true}
1.3 next
一般情况下,next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。当 next 传入参数的时候,该参数会作为上一步yield的返回值。
next() 的结果始终是一个具有两个属性的对象:
value: 产出的(yielded)的值。done: 如果 generator 函数已执行完成则为true,否则为false。
function* sendParameter() {
console.log("start");
var x = yield "2"; // x值为undefined,因为yield表达式是没有返回值的
console.log("one:" + x);
var y = yield "3";
console.log("two:" + y);
console.log("total:" + (x + y));
}
next不传参
var sendp1 = sendParameter();
sendp1.next();
// start
// {value: "2", done: false}
sendp1.next();
// one:undefined
// {value: "3", done: false}
sendp1.next();
// two:undefined
// total:NaN
// {value: undefined, done: true}
next传参
var sendp2 = sendParameter();
sendp2.next();
// start
// {value: "2", done: false}
sendp2.next(20);
// one:20
// {value: "3", done: false}
sendp2.next(30);
// two:30
// total:50
// {value: undefined, done: true}
第一次执行next也可以传入一个参数,但由于它没有上一次yield所以没有任何东西能够接受它,会被忽略掉,所以没有什么意义。
需要注意的是,
yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
2. Generator深入理解
2.1 for...of
进行下面所有的部分之前我们先说一说迭代器,看到现在,我们都知道generator函数执行完返回的是一个迭代器。在ES6中同样提供了一种新的迭代方式for...of,for...of可以帮助我们直接迭代出每个的值,在数组中它像这样。
for (var i of ['a', 'b', 'c']) {
console.log(i);
}
// 输出结果
// a
// b
// c
下面我们用我们的generator迭代器试试
function *foo() {
yield 1;
yield 2;
yield 3;
return 4;
}
// 获取迭代器
var it = foo();
for(var i of it) {
console.log(i);
}
// 输出结果
// 1
// 2
// 3
现在我们发现for...of会直接取出我们每一次计算返回的值,直到done: true。这里注意,我们的4没有打印出来,说明for...of迭代,是不包括done为true的时候的值的。
下面我们提一个新的问题,如果在generator中执行generator会怎么样?这里我们先认识一个新的语法yield *,这个语法可以让我们在yield跟一个generator执行器,当yield遇到一个新的generator需要执行,它会先将这个新的generator执行完,再继续执行我们当前的generator。这样说可能不太好理解,我们看代码。
function *foo() {
yield 2;
yield 3;
yield 4;
}
function * bar() {
yield 1;
yield *foo();
yield 5;
}
for ( var v of bar()) {
console.log(v);
}
这里有两个generator我们在bar中执行了foo,我们使用了yield *来执行foo,这里的执行顺序会是yield 1,然后遇到foo进入foo中,继续执行foo中的yield 2直到foo执行完毕。然后继续回到bar中执行yield 5所以最后的执行结果是:
1
2
3
4
5
2.2 异步请求
我们上面的例子一直都是同步的,但实际上我们的应用是在异步中,我们现在来看看异步中怎么应用。
案例1:1s后输出111,再过2s后输出222,再过3s后输出333
-
传统方式:嵌套太多,代码复杂,产生 回调地狱。
setTimeout(() => { console.log(111); setTimeout(() => { console.log(222); setTimeout(() => { console.log(333); }, 3000); }, 2000); }, 1000);Copy to clipboardErrorCopied -
生成器实现:结构简洁明了
//1.创建3个函数输出结果 function one() { setTimeout(() => { console.log(111); //5.第一个函数执行完毕后自动调用下一个yield iter.next(); }, 1000); } function two() { setTimeout(() => { console.log(222); iter.next(); }, 2000); } function three() { setTimeout(() => { console.log(333); }, 3000); } //2.创建生成器函数 function* generator() { yield one(); yield two(); yield three(); } //3.执行生成器函数得到生成器对象 let iter = generator(); //4.执行第一个yield iter.next();
案例2:生成器函数模拟每隔1s获取商品数据
//1.创建3个函数模拟获取从后端传递的数据
function getUsers() {
setTimeout(() => {
let users = "用户数据";
iter.next(users); // 5.第一个函数执行得到了用户数据,将其作为next的参数传递过去,会被当做上一个yield的返回值
}, 1000);
}
function getOrders() {
setTimeout(() => {
let orders = "订单数据";
iter.next(orders);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let goods = "商品数据";
iter.next(goods);
}, 1000);
}
//2.创建生成器函数
function* generator() {
// 6. 用users接收yield的返回值(next参数)
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
//3.执行生成器函数得到生成器对象
let iter = generator();
//4.执行第一个yield
iter.next();
2.3 教程(建议看)
十二、 redux中异步操作-saga
- redux-saga是另一个比较常用在redux发送异步请求的中间件,它的使用更加的灵活。
- Redux-saga的使用步骤如下
- 安装redux-saga
yarn add redux-saga - 集成redux-saga中间件
- 导入创建中间件的函数;
- 通过创建中间件的函数,创建中间件,并且放到applyMiddleware函数中;
- 启动中间件的监听过程,并且传入要监听的saga;
- saga.js文件的编写
- takeEvery:可以传入多个监听的actionType,每一个都可以被执行(对应有一个takeLatest,会取消前面的)
- put:在saga中派发action不再是通过dispatch,而是通过 put;
- all:可以在yield的时候put多个action;
- 安装redux-saga