likes
comments
collection
share

【前端丛林】React这样服用,效果更佳(18)

作者站长头像
站长
· 阅读数 41

前言

哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是我们冒险的第十七天,昨天给大家介绍一下Redux的中间件,今天我们继续深入Redux中间件,介绍一下redux-saga,看看到底有哪些作用,让我们探寻其中的奥秘。话不多说,开始我们的冒险之旅吧!

1. redux-saga

  • redux-saga 是一个 redux 的中间件,而中间件的作用是为 redux 提供额外的功能
  • 在 reducers 中的所有操作都是同步的并且是纯粹的,即 reducer 都是纯函数,纯函数是指一个函数的返回结果只依赖于它的参数,并且在执行过程中不会对外部产生副作用,即给它传什么,就吐出什么
  • 但是在实际的应用开发中,我们希望做一些异步的(如Ajax请求)且不纯粹的操作(如改变外部的状态),这些在函数式编程范式中被称为“副作用”
  • redux-saga 就是用来处理上述副作用(异步任务)的一个中间件。它是一个接收事件,并可能触发新事件的过程管理者,为你的应用管理复杂的流程

2. redux-saga工作原理

  • sages 采用 Generator 函数来 yield Effects(包含指令的文本对象)
  • Generator 函数的作用是可以暂停执行,再次执行的时候从上次暂停的地方继续执行
  • Effect 是一个简单的对象,该对象包含了一些给 middleware 解释执行的信息
  • 你可以通过使用 effects API 如 fork,call,take,put,cancel 等来创建 Effect

3. redux-saga分类

  • worker saga 做实际的工作,如调用API,进行异步请求,获取异步封装结果
  • watcher saga 监听被dispatch的actions,当接受到action或者知道其被触发时,调用worker执行任务
  • root saga 立即启动saga的唯一入口

4. 构建项目

初始化项目

cnpm install create-react-app -g
create-react-app redux-saga-start
cd redux-saga-start
cnpm i redux react-redux redux-saga tape --save

5. 跑通saga

5.1 src\index.js

src\index.js

import store from './store';

5.2 store\index.js

src\store\index.js

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga';
//首先我们引入 ./sagas 模块中的 Saga。然后使用 redux-saga 模块的 createSagaMiddleware 工厂函数来创建一个 Saga middleware
import { helloSaga } from './sagas';
let sagaMiddleware = createSagaMiddleware();
//运行 helloSaga 之前,我们必须使用 applyMiddleware 将 middleware 连接至 Store。然后使用 sagaMiddleware.run(helloSaga) 运行 Saga。
let store = applyMiddleware(sagaMiddleware)(createStore)(reducer);
sagaMiddleware.run(helloSaga);
export default store;

5.3 store\reducer.js

src\store\reducer.js

export default function (state,action) {}

5.4 store\sagas.js

src\store\sagas.js

//helloSaga它只是打印了一条消息,然后退出。
export function* helloSaga() {
    console.log('Hello Saga!');
}

6. 异步计数器

6.1 components/Counter.js

src/components/Counter.js

import React, { Component } from 'react'
import { connect } from 'react-redux';
import actions from '../store/actions';
class Counter extends Component {
    render() {
        return (
            <div>
                <p>{this.props.number}</p>
                <button onClick={this.props.incrementAsync}>+</button>
            </div>
        )
    }
}
export default connect(
    state => state,
    actions
)(Counter);

6.2 src/index.js

src/index.js

import React from 'react'
import ReactDOM from 'react-dom';
import Counter from './components/Counter';
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(<Provider store={store}>
    <Counter />
</Provider>, document.querySelector('#root'));

6.3 store/action-types.js

src/store/action-types.js

export const INCREMENT='INCREMENT';
export const INCREMENT_ASYNC='INCREMENT_ASYNC';

6.4 store/actions.js

src/store/actions.js

import * as types from './action-types';
export default {
    incrementAsync() {
        return { type: types.INCREMENT_ASYNC }
    }
}

6.5 src/store/index.js

src/store/index.js

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga';
//首先我们引入 ./sagas 模块中的 Saga。然后使用 redux-saga 模块的 createSagaMiddleware 工厂函数来创建一个 Saga middleware
import rootSaga from './sagas';
let sagaMiddleware = createSagaMiddleware();
//运行 rootSaga 之前,我们必须使用 applyMiddleware 将 middleware 连接至 Store。然后使用 sagaMiddleware.run(helloSaga) 运行 Saga。
let store = applyMiddleware(sagaMiddleware)(createStore)(reducer);
sagaMiddleware.run(rootSaga);
export default store;

6.6 store/reducer.js

src/store/reducer.js

import * as types from './action-types';
export default function (state = { number: 0 }, action) {
    switch (action.type) {
        case types.INCREMENT:
            return { number: state.number + 1 };
        default:
            return state;
    }
}

6.7 store/sagas.js

src/store/sagas.js

import { delay, all, put, takeEvery } from 'redux-saga/effects'
//worker Saga: 将执行异步的 increment 任务
//incrementAsync Saga 通过 delay(1000) 延迟了 1 秒钟,然后 dispatch 一个叫 INCREMENT 的 action。
export function* incrementAsync() {
    //工具函数 delay,这个函数返回一个延迟 1 秒再 resolve 的 Promise 我们将使用这个函数去 block(阻塞) Generator
    //Sagas 被实现为 Generator functions,它会 yield 对象到 redux-saga middleware。 被 yield 的对象都是一类指令,指令可被 middleware 解释执行。当 middleware 取得一个 yield 后的 Promise,middleware 会暂停 Saga,直到 Promise 完成
    //incrementAsync 这个 Saga 会暂停直到 delay 返回的 Promise 被 resolve,这个 Promise 将在 1 秒后resolve
    //当 middleware 拿到一个被 Saga yield 的 Effect,它会暂停 Saga,直到 Effect 执行完成,然后 Saga会再次被恢复
    yield delay(1000)
    //一旦 Promise 被 resolve,middleware 会恢复 Saga 接着执行,直到遇到下一个 yield
    //在这里下一个语句是另一个被 yield 的对象:调用 put({type: 'INCREMENT'}) 的结果,意思是告诉 middleware 发起一个 INCREMENT 的 action
    //put 就是我们称作 Effect 的一个例子。Effects 是一些简单 Javascript 对象,包含了要被 middleware 执行的指令
    yield put({ type: 'INCREMENT' })
}
//takeEvery,用于监听所有的 INCREMENT_ASYNC action,并在 action 被匹配时执行 incrementAsync 任务
export function* watchIncrementAsync() {
    yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}
export function* helloSaga() {
    console.log('Hello Saga!');
}
export default function* rootSaga() {
    //这个 Saga yield 了一个数组,值是调用 helloSaga 和 watchIncrementAsync 两个 Saga 的结果。意思是说这两个 Generators 将会同时启动
    yield all([
        helloSaga(),
        watchIncrementAsync()
    ])
}

结尾

好啦,这期的前端丛林大冒险先到这里啦!这期我们了解了redux-saga的基本概念和使用。这期内容不太容易理解,大家一定要多读几遍,要好好啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!

转载自:https://juejin.cn/post/7160483327127289870
评论
请登录