前端開發中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>