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)从名字本身上来看也更性感
你会怎么选?欢迎留言告诉我你的想法。