redux-thunk 和 redux-saga 的区别?

redux-thunk 和 redux-saga 的区别?

 

毋庸置疑,如果需要用到 side effect 异步操作,redux-thunk 和 redux-saga 绝对是目前两个最受欢迎的中间件插件。那么他们之间最主要的区别是什么?

 

这就要首先说一说使用 redux 时异步操作出现的具体时机。

如下如所示,当出发一个 action 会经过中间件 middlewares,这时所有的 side effect 操作,例如调用 api 获取数据等等都将在这里完成。然后再经由 reducer 更新 state,最后传递到 view 完成 MVC 的数据流循环。

所有的调取数据都在 api 那部分完成

 

redux-thunk 解决方案

 

首先 thunk 来源自 think 的”过去式“ -- 作者非常特别的幽默感。主要意思就是声明一个函数来代替表达式,这样就可以将执行求值操作(evaluation)延迟到所需要的时刻。

// calculation of 1 + 2 is immediate
// x === 3
let x = 1 + 2;

// calculation of 1 + 2 is delayed
// foo can be called later to perform the calculation
// foo is a thunk!
let foo = () => 1 + 2;

 

注册插件很简单,大致代码如下

// Note: this API requires redux@>=3.1.0
const store = createStore(rootReducer, applyMiddleware(thunk));

ReactDOM.render(
  <Provider store={store}>
    <Routes />
  </Provider>,
  document.getElementById('root')
);

 

Reducer 也非常简单,和原来一模一样

export default (state = defaultState, action) => {
  switch (action.type) {
    case REMOTE_DATA_RECEIVED:
      return {
        ...state,
        data: action.data
      };
    default:
      return state;
  }
};

 

不同之处在于 action,普通的 action 大多长这样

export function toggleTodo(index) {
  return { type: TOGGLE_TODO, index }
}

 

而 redux-thunk 的 action 可以是一 异步的 higher order function 高阶函数

export const fetchData = args => async (dispatch, getState) => {
  const state = getState();
  const url = 'https://jsonplaceholder.typicode.com/users/' + args;

  try {
    const response = await fetch(url)
      .then(resp => {
        return resp;
      })
      .then(resp => resp.json());

    dispatch({
      type: REMOTE_DATA_RECEIVED,
      data: response
    });
  } catch (error) {
    console.log(error);
  }
};

 

其他的地方关于 view 等都是一样的操作。

 

redux-saga 解决方案

 

注册插件大家都很类似

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './root-reducer';
import { watchFetchSaga } from './saga/fetchData.saga';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(watchFetchSaga);

...

 

但是 saga 使用的仍然是普通的 action

// 这个 action 将由 saga 监听,并且出发 side effect 异步加载 api 操作
export const fetchData = () => ({
  type:  "START_FETCH_DATA"
});

// 这个 action 将由 saga 发出
export const fetchSuccess = data => ({
  type: "REMOTE_DATA_RECEIVED",
  payload: data
});

 

接下来就是注册 saga 相关 side effect 操作。下面的文件是 fetchData.saga.js

import { takeLatest, put } from "redux-saga/effects";

function* fetchDataSaga(action){
  try {
    const response = yield fetch(action.url);
    const data = yield response.json()
    yield put(fetchSuccess(data));
  } catch (error) {
    console.log(error);
  }
}

export default function* watchFetchSaga(){
  // saga 将监听此事件,takeLatest 表示仅仅只监听最新的此事件
  yield takeLatest("START_FETCH_DATA", fetchDataSaga)
}

 

总结

 

可以看到 saga 自己基本上完全弄了一套 asyc 的事件监听机制。虽然好的一方面是将来可以扩展成 worker 相关的模块,甚至可以做到 multiple threads 同时执行,但代码量大大增加。如果只是普通的 application,用 redux-thunk 足够。

 

redux-thunk 是 2015-7-13 发布的第一个版本,而 redux-saga 是 2015-12-2 发布的第一个版本。他们基本上都是在同一年被创造出来。

 

下面是两者周下载量。

redux-thunkredux-saga

 

除了上面的数据我再来说说自己的使用感受。这两款插件我均正式使用过,而且都是用于商业软件开发。从我自己的使用体验来看 redux-thunk 更简单,和 redux 本身联系地更紧密。尤其是整个生态都向函数式编程靠拢的今天,redux-thunk 的高阶函数看上去更加契合这个闭环。

 

如果有的选,我肯定选 redux-thunk。因为

1)简单才是王道

2)从名字本身上来看也更性感

 

你会怎么选?欢迎留言告诉我你的想法。

 

posted on 2021-12-14 20:33  漫思  阅读(544)  评论(0编辑  收藏  举报

导航