Flux思想、异步thunk、 Generator | 8月更文挑战

418 阅读6分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

Flux 思想

数据的单项流动,仰赖MVVM模式

image.png

action creator

创造熊猫的,叫做panda creator

创造小狗的,叫做dog creator

比如下面的箭头函数就是一个action creator

() => ({“type” : “ADD”})

总结:action creator是个函数,返回Action

connect装饰器中使用action creator

export default connect(
    ({counterReducer}) => ({
        v : counterReducer.v
    }),
    {
        add(){
            return {"type" : "ADD"}
        },
        minus(){
            return {"type" : "MINUS"}
        }
    }
)(App)

add()和minus()的键 都是Action creator,罗列一个个返回Action的函数。

创建counterAction.js 文件

向外暴露两个action creator:

export const add = () => ({"type" : "ADD"});
export const minus = () => ({"type" : "MINUS"});

组件只需要引入 counterAction.js文件,并修改connect函数

export default connect(
    ({counterReducer}) => ({
        v : counterReducer.v
    }),
    {
        add,
        minus
    }
)(App)

bindActionCreators 绑定action creator

调用 需要使用 命名空间

import React from 'react';
import { connect } from "react-redux";
import * as counterActions from "./actions/counterActions";
import {bindActionCreators} from "redux";
 
class App extends React.Component{
    constructor(){
        super();
 
    }
 
    render(){
        return(
            <div>
                <h1>{this.props.v}</h1>

                <button onClick={()=>{
                    this.props.counterActions.add();
                }}>按我+</button>

                <button onClick={() => {
                    this.props.counterActions.minus();
                }}>按我-</button>
            </div>
        )
    }
}
export default connect(
    ({counterReducer}) => ({
        v : counterReducer.v
    }),
    (dispatch) => ({
        counterActions: bindActionCreators(counterActions , dispatch)
    })
)(App)

异步

redux-thunk

thunk是形式转换程序的意思

安装依赖

npm install --save redux-thunk

安装thunk之后,action文件中就可以写 两个括号了,如下

export const addAfter2000ms = () => () => {}
export const addAfter2000ms = () => (dispatch) => {}

第二个括号中系统附送 dispatch

总结
1)thunk是一个语法转换程序,装了之后,可以在action文件中写() => () => {}形式
2)异步真正在action文件中执行
3)保持了组件文件的美观、整齐
4)reducer中不额外增加if分支。

async 和 redux-thunk结合使用

import axios from 'axios';
const GETDATA = "GETDATA"
const getData = (url:any, value:any) => async (dispatch:any) => {
    let results = await axios.post(url, value, {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    })
    .then(res => res.data.data)
    .catch(error => {
        console.log(error);
    });
    dispatch(fetchData(results));
}
const fetchData = (results:any) => ({type: GETDATA, results})

export {
    getData
}

redux-saga

saga是英语“传奇”的意思。它的思想是“拦截”。

官网:redux-saga.js.org/

redux-saga的大体过程如下: image

安装依赖

npm install --save redux-saga

需要我们补充babel的插件。babel要想处理加星函数、Promise等,必须要装babel-plugin-transform-runtime。

npm install --save-dev babel-plugin-transform-runtime

修改webpack.config.js: 找到这一行: 添加一个插件 transform-runtime

plugins: ['transform-object-rest-spread','transform-runtime']

创建saga.js

export const helloSaga = function* () {
    console.log('我是saga,你好!')
}

函数有一个星,叫做产生器

修改main.js 入口文件 参考手册 redux-saga.js.org/docs/introd…

总结:
1)组件、action、reducer文件都保持了形式上的美感,没有破坏代码整齐性。没有两个()的action,保持了高度的整齐性;
2)真正实现异步在saga文件中,而thunk是action文件。
3)saga的思路是拦截。thunk的思路是“形式转换”,让异步的action写两个圆括号。因为saga的思路好,所以它“易于插拔”,感觉“在外部”管理整个业务逻辑,是一个独立的部分。
4)saga结束之后,最终的产物是一次put,表示转发action。

Promise

同步函数的特点

  1. 函数必须所有语句执行完毕,再执行后面的语句
  2. 函数体内用return返回值
  3. 函数调用时,等号左侧用变量接收

异步函数的特点:

  1. 异步函数会先之后后面的语句,等后面语句清空(同步栈执行完毕)、异步也做完了,执行回调函数;
  2. 函数体内没有return这个词语,必须用回调函数来返回值;
  3. 函数调用的时候,不能写等号,不能用变量来接收值,必须用回调函数来接手值。

写一个异步函数,2000ms返回一个随机数

function getNumberAfter2000ms(cb){
    setTimeout(function(){
        cb(Math.random()); 
    },2000);
}

getNumberAfter2000ms(function(a){
    console.log(a);
});
console.log("你好");

回调黑洞(callback hell)

调用3次getNumberAfter2000ms 用6s得到3个数

getNumberAfter2000ms(function(a){
    console.log(a);
    getNumberAfter2000ms(function (b) {
        console.log(b);
        getNumberAfter2000ms(function (c) {
            console.log(c);
        });
    });
});

认识Promise

  • Promise 是ES6中提供的一个语法糖,可以简化异步函数的书写。
  • 是ES6提供的内置构造函数

封装一个Promise

写一个异步函数,函数返回Promise实例,而Promise实例圆心上有then方法

整体结构

function getNumberAfter2000ms(){
    return new Promise();
}

getNumberAfter2000ms().then();

实例化Promise的时候,必须传入一个函数 (resolve,reject)=>{}。形参resolve表示成功,reject表示失败。 resolve就是成功时候回调,由then里面传入。

function getNumberAfter2000ms(){
    return new Promise((resolve , reject) => {
        setTimeout(function(){
            resolve(Math.random());
        },2000);
    });
}

getNumberAfter2000ms().then(function(a){
    console.log(a);
});

此时就可以链式调用一个异步函数,就不需要使用 毁掉黑洞了。

getNumberAfter2000ms()
.then(function(a){
    console.log(a);
    return getNumberAfter2000ms();
})
.then(function (a) {
    console.log(a);
    return getNumberAfter2000ms();
})
.then(function (a) {
    console.log(a);
    return getNumberAfter2000ms();
});

自己封装一个 promise 函数

class ChengNuo{
    constructor(fn){
        this.fn = fn;
    }
    then(cb){
        this.fn(cb)
    }
}
function getAfter2s(){
    return new ChengNuo((resolve,reject) => {
        setTimeout(function(){
            resolve(Math.random())
        },1000)
    })
}
getAfter2s().then(function(b){
    console.log(b);
    return getAfter2s()
})

fetch()函数 --兼容性差

表示 “请求、得到”的意思。天生返回 Promose的实例

原生fetch 用的不是XMLHttpRequest对象,即 fetch不是ajax。

axios

功能: 使用XMLHttpRequest对象,模拟了Promise的语法,和fetch语法一样。 安装依赖:

npm install --save axios

使用方法:

import axios from "axios";

function* addServer() {
    const {m} = yield axios.get("/api/shu").then(data=>data.data);
    yield put({ "type": "ADD" , m});
}

async / await

ES7中提供了async/await,可以写同步函数的写法调用异步函数。

async 只能加在function关键字的前面,不能加在箭头函数之前。

function getNumberAfter2000ms(){
    return new Promise((resolve , reject) => {
        setTimeout(function(){
            resolve(Math.random());
        },2000);
    });
}

async function main(){
    const a = await getNumberAfter2000ms().then(data => data);
    console.log(a);
    const b = await getNumberAfter2000ms().then(data => data);
    console.log(b);
    const c = await getNumberAfter2000ms().then(data => data);
    console.log(c);
}

main();

async的函数里面,可以用await标注语句。await后面必须跟着一个返回Promise实例的函数then方法的执行,then的函数返回值,将被自动被await左边的变量接收(这是c++浏览器底层封装)

await表示等待,它将等待这个异步函数执行完毕。

Generator 加星函数

ES8推出的,非常新,Generator叫做产生器,库打断点的函数,可以有多个返回值的函数。

例子

function* gen(){
    console.log("哈哈");
    yield "A";
    console.log("嘻嘻");
    yield "B";
    console.log("么么哒");
    yield "C";
}
var I = gen(); //激活这个加星函数

I.next()     //输出哈哈
I.next()     //输出嘻嘻
I.next()      //输出么么哒

yield表示“产出”,就是函数的断点。

gen()函数能够让虚拟“游标”就位,在 function* 下一行就位。此时i就可以调用next()方法,表示执行函数到最近的yield。

yield后面的值,也可以被看见

console.log(g.next().value);           //输出A

加星函数和Promise搭配堪称完美。

function getNumberAfter2000ms(){
    return new Promise((resolve) => {
        setTimeout(function(){
            resolve(Math.random())
        },2000);
    });
}

function* gen(){
    yield getNumberAfter2000ms();
    yield getNumberAfter2000ms();
    yield getNumberAfter2000ms();
}

var i = gen();  //激活,准备游标

i.next().value
.then(data=>{
    console.log(data);
    return i.next().value;
})
.then(data => {
    console.log(data);
    return i.next().value;
})
.then(data => {
    console.log(data);
})

封装一个delay函数

function delay(time){
    return new Promise((resolve)=>{
        setTimeout(function(){
            resolve()
        },time)
    })
}

function* gen(){
    yield delay(2000)
    yield Math.random()
}

感谢

谢谢各位在百忙之中点开这篇文章,希望对你们能有所帮助,如有问题欢迎各位大佬指正。

如果觉得写得还行的话,那就点个赞吧。