redux-saga是什么?

家喻户晓, redux-saga 是一个中间件。所谓的中间件就是给redux提供额定性能的, 简而言之 , 也就是对redux中的dispatch, 加上一些性能, 进行包装。

咱们为什么要应用redux-saga呢?

在redux中, 如果咱们dispatch一个action之后 , 会调用reducer函数 。

 function dispatch(action) {    state = reducer(state, action);    listeners.forEach(l => l());    return action;  }

而后reducer是一个纯函数, 没有副作用 ,当咱们须要进行异步申请的时候,这个时候redux就不够用了 , 因而咱们须要借助于redux-saga。

redux-saga如何进行裁减redux的性能呢?

中间件的根本格局

咱们在裁减中间件的时候会应用一个applyMiddleware的函数, 对原有的store.dispatch进行裁减。

function applyMiddleare(...middlewares) {  return function (createStore) {    return function (reducer, preloadedState) {      let store = createStore(reducer, preloadedState);      let dispatch;      let middlewareAPI = {        getState: store.getState,        dispatch: (action) => dispatch(action),      };      let chain = middlewares.map((middleware) => middleware(middlewareAPI));      dispatch = compose(...chain)(store.dispatch);      return {        ...store,        dispatch,      };    };  };}//compose函数是:function compose(funcs){return funcs.map((a, b) => (...args) => a(b(...args)))}

由上面这段代码能够看出一个中间件, 是承受一个middleAPI也就是store,以及上一次革新后的dispatch, 而后返回一个新的革新后的dispatch。

 let chain = middlewares.map((middleware) => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch);

所以一个中间件的格局应该是:

function middleware(store) {  return (lastDispatch) => {    return (action) => {      //进行新的革新      //比方logger中间件是加上consolelog 而后lastDispatch      // 比方redux-thunk  是如果action是一个函数的话应该如何解决    };  };}

redux-saga基本原理:

saga采纳generator函数来yield effect,generator函数的作用是能够暂停执行, 下次再从上次暂停的中央继续执行。

redux-saga根本根本应用和具体实现:

v redux-saga中saga分类:
  • rootsaga,也就是saga的入口
  • 监听saga, 也就是watchsaga,个别通过yieldtake来监听
  • 具体执行的saga, 通过put fork cps等等来执行

    // 执行sagafunction * workerSaga(){yield put({type:actionTypes.ADD});}// 监听sagafunction * watcherSaga(){yield take(actionTypes.ADD_SYNC);yield workerSaga();}// 入口sagaexport default function* rootSaga() {yield watcherSaga();} 

咱们晓得在应用redux-saga的时候咱们应用createSagaMiddleware来进行创立一个saga函数中间件 , 而后在saga函数增加了一个run属性,而后在调用了applyMiddleware之后执行saga.run(rootsaga)。saga.run的时候咱们就把入口saga放到了redux-saga的逻辑程序外面进行执行了。当初咱们想一下这个过程应该怎么去实现:

首先, createSagaMiddleware就是创立saga的中间件, 依照下面我所说的中间件的格局, 所以 createSagaMiddleware应该这样写

function createSagaMiddleware() {  function sagaMiddleware({ getState, dispatch }) {    return function (lastDispatch) {      return function (action) {        const result = lastDispatch(action);        return result;      }    }  }  return sagaMiddleware;}

而后须要给sagaMiddleware增加run函数, 在run函数中咱们执行了一个runsaga的函数。这是一个相似于co的库, 递归调用generator函数 , 直到it.next()返回的done值是false的时候才会完结。在这期间会对不同的effect的type进行不同的解决因而实现了effect的异步执行。

sagaMiddleware.run= function (...args) {runSaga(...args)}export default function runSaga(env, saga) {    let { channel, dispatch } = env;    let it = typeof saga === 'function'?saga():saga; // yield fork 的时候间接是执行了一遍runsaga()// 所以saga可能就不是一个function    function next(value) {        let {value:effect,done} = it.next(value);        if (!done) {            if(typeof effect[Symbol.iterator] === 'function'){// 这种是yield 前面接了一个saga的状况,如yield watchSaga()                runSaga(env,effect);                next();//不会阻止以后saga持续向后走            }else if (effect instanceof Promise) {// 如果yield 了一个new promise                 effect.then(next);            }else{                switch (effect.type) {                    case effectTypes.TAKE:                      // ...                    case effectTypes.PUT:                         // ...                        break;                    default:                        break;               }            }        }    }    next();}

通过effect.type对不同的类型进行函数的解决。

take: 监听一次

用法:

 yield take(actiontype)

监听一次, 而后持续向下执行。

case effectTypes.TAKE:channel.once(effect.actionType,next);

其中channel是一个相似于node EventEmitter的公布订阅模型, 咱们能够实现一个。

function channel() {  let listeners = [];  function once(type, listener) {    listener.type = type;    listener.cancel = function () {      listeners = listeners.filter((item) => item !== listener);    };    listeners.push(listener);  }  function put(action) {    for (let i = 0; i < listeners.length; i++) {      if (listeners[i].type === action.type) {        //只有一次监听        listeners[i](action);        listeners[i].cancel();      }    }  }  return {    once,    put,  };}

那咱们什么时候去派发函数让take去监听呢?没错就是咱们在enhance dispatch的时候进行派发动作的,因而咱们createSagaMiddleware 返回的dispatch中退出派发的动作, 进而可能让take进行监听。

function createSagaMiddleware() {  function sagaMiddleware({ getState, dispatch }) {    return function (lastDispatch) {      return function (action) {        const result = lastDispatch(action);        channel.put(action);        return result;      }    }  }  return sagaMiddleware;}
fork 不会阻塞的runsaga

用法: yield fork(saga) // 返回一个task对象 , 对象里有cancel属性

case effectTypes.FORK:   runSaga(env,effect.saga);      next();

所以就算此时yield fork没有执行实现 , 这个时候也会持续相加执行下一个yield,因而不会梗塞, 这也是和put的区别。

takeEvery: 基于fork和take进行实现。

用法: yield takeEvery(actionType)
原理是每当执行takeEvery的时候就会从新开启一个新的过程,新的过程是一个while true循环, 每个循环都会take监听

function takeEvery(type, saga){  function *sagaHelper(){    while(true){     yield take(type)      yield fork(saga)    }  }  fork(sagaHelper)}
call 能够调用promise函数

用法: yield call(promiseFn)

case effectTypes.CALL: effect.fn(...effect.args).then(next);  break;
cps 回调函数的解决形式

用法:

 yield cps(fn, params1, params2)function fn(param1, param2, callback){callback(null,data)}

跟promise的区别在于应用了callback的解决形式,如果呈现error的话, 那么next执行的时候就it.throw()抛出异样。

case effectTypes.CPS:      if(err){            effect.fn(...effect.args,(err,data)=>{                           }else{                next(err,true);          next(data);      }  });  break; 
all 都执行实现之后才持续向下执行

用法: yield all ([iterator1,iterator2])

 case effectTypes.ALL:    const { iterators } = effect;    let result  =[];    let count  =  0;    iterators.forEach((iterator, index) => {      runSaga(env, iterator,(data)=> {        result[index] =data;        if(++count===iterators.length){          next(result)        }      })

总结:

在redux-saga中, yield 了一个effect之后, 会在effect.js中转换成相应的effecttype,而后通过runsaga这个函数来进行不同的转换。 runsaga这个函数相似于co原理 , 递归调用next函数 , 终止条件是 it.next()返回的done是true值。