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)從名字本身上來看也更性感

 

你會怎麼選?歡迎留言告訴我你的想法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章