前端開發中React + Redux 是大部分專案的標配,Redux也是我喜歡的庫之一,他的原始碼也拜讀過幾遍,每次都有很多收穫,尤其他的中介軟體設計模式,對自己封裝一些庫提供了思想上的指導。

Redux工作流程如下圖:

程式碼實現

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script> function compose(middlewares) {
// 相當於fn1(fn2(fn3(...ages)))
// 所以中介軟體是從右到左執行
return middlewares.reduce((a, b) => (...args) => a(b(...args)));
} function createStore(reducers, middlewares) {
const currentState = {};
let dispatch = function (action) {
Object.keys(reducers).forEach(function (key) {
currentState[key] = reducers[key](currentState[key], action);
})
} function getState() {
return currentState;
} if (middlewares) {
// 通過閉包,把當前的dispatch, getState傳遞到中介軟體中
// (action, ...args) => dispatch(action, ...args) 這裡需要使用函式實時獲取dispatch, 可以理解為next等價
const chain = middlewares.map(middleware => middleware((action, ...args) => dispatch(action, ...args), getState))
// 傳遞dispatch標識next
dispatch = compose(chain)(dispatch);
} dispatch({type: 'INIT'});
return {
dispatch,
getState
}
} // log 中介軟體
function logMiddleware(dispatch, getState) {
return function (next) {
return function (action) {
console.log(`當前的age: ${getState().userInfo ? getState().userInfo.age : null}, 將要更新age為:${JSON.stringify(action)}`);
return next(action);
}
}
} // thunk 中介軟體可以非同步請求
function thunkMiddleware(dispatch, getState) {
return function (next) {
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
}
}
} const store = createStore({
userInfo: function (prevState = {age: 1, name: 'initName'}, action) {
switch (action.type) {
case 'SET':
return {...prevState, ...action.value};
default:
return prevState;
}
}
}, [thunkMiddleware, logMiddleware]); console.log('init', store.getState().userInfo.name, store.getState().userInfo.age); store.dispatch({type: 'SET', value: {age: 18}}); store.dispatch(function (dispatch, getState) {
// 模擬非同步請求
setTimeout(function () {
dispatch({type: 'SET', value: {age: getState().userInfo.age + 1}})
}, 2000);
}); console.log(store.getState().userInfo.name, store.getState().userInfo.age); store.dispatch({type: 'SET', value: {name: 'xiaoLi'}}); console.log(store.getState().userInfo.name, store.getState().userInfo.age); setTimeout(function () {
console.log(store.getState().userInfo.name, store.getState().userInfo.age);
}, 2000); </script>
</body>
</html>