redux 总结
redux 是前端的一种数据解决方案。redux 由三部分组成,action、reducer 和 store。
其中 store 用于保存数据,action 用来描述如何改变数据,reducer 是用于改变数据的处理函数。
三者间的关系:

action
- 必须是一个 plain-object
- 通常,使用
payload 属性表示附加数据
action 中必须有 type 属性,描述操作的类型,该属性可以是任何类型
action 创建函数
- 为了方便传递
action,通常会使用 action 创建函数来创建 action
action 创建函数应为纯函数
bindActionCreators
- 为了方便利用
action 创建函数来分发 action
- 参数 1:
action 创建函数 / action 创建函数集合(对象)
- 参数 2:
store.dispatch
- 返回值:增强的
action 创建函数,创建后自动分发
reducer
-
reducer 是一个接收两个参数:state 和 action 的函数
-
reducer 被调用的时机:
- 通过
store.dispatch,分发了一个 action
- 当创建一个
store 的时候,可以利用这一点,用 reducer 初始化状态:
- 创建仓库时,不传递任何默认状态
- 给
reducer 的参数 state 设置一个默认值
-
reducer 内部通常使用 switch 来判断 type 值
-
reducer 必须是一个没有副作用的纯函数
-
大中型项目中需要对 reducer 进行细分,redux 提供了方法 combineReducers 帮助我们方便的合并 reducer
store
dispatch:分发一个 action
getState:得到仓库中当前的状态
replaceReducer:替换掉当前的 reducer
subscribe:注册一个监听器(无参),分发一个 action 之后会运行,该函数会返回一个函数,用于取消监听
createStore
根据 redux 的基本使用方法和要求我们可以实现一个不包括中间件的 createStore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| function createStore(reducer, defaultState) { let currentReducer = reducer, currentState = defaultState;
const listeners = [];
const dispatch = (action) => { checkAction(action); currentState = currentReducer(currentState, action); listeners.forEach((listener) => listener()); };
const subscribe = (listener) => { listeners.push(listener); return () => { const index = listeners.indexOf(listener); if (index === -1) { return; } listeners.splice(index, 1); }; };
const getState = () => currentState;
dispatch({ type: "@@redux/INIT" + Math.random().toString(36).slice(2, 6).split("").join("."), });
return { dispatch, getState, subscribe, }; }
|
combineReducer
combineReducer 所做的事就是将我们小的 reducer 合并成一个大的,并且会在初始就触发两次 reducer 来验证我们的 reducer 是否存在问题,不能返回 undefined。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function combineReducers(reducers) { assertReducerShape(reducers); return combine(reducers); }
const randomString = () => Math.random().toString(36).slice(2, 6).split("").join(".");
function assertReducerShape(reducers) { for (const r in reducers) { const reducer = reducers[r]; if ( reducer(undefined, { type: "@@redux/INIT" + randomString() }) === undefined || reducer(undefined, { type: "@@redux/PROBE_UNKNOWN_ACTION" + randomString(), }) === undefined ) { throw new TypeError("reducers must not return undefined"); } } } function combine(reducers) { return (state = {}, action) => { const newState = {}; for (const r in reducers) { newState[r] = reducers[r](state[r], action); } return newState; }; }
|
bindActionCreator
bindActionCreator 既可以接收创建函数也可以接收多个创建函数,所以需要分情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function bindActionCreates(actionCreators, dispatch) { if (typeof actionCreators === "function") { return bindActionCreator(actionCreators, dispatch); } const newActionCreators = {}; for (const creator in actionCreators) { const actionCreator = actionCreators[creator]; if (typeof actionCreator === "function") { newActionCreators[creator] = bindActionCreator(actionCreator, dispatch); } } return newActionCreators; }
function bindActionCreator(actionCreator, dispatch) { return function (...args) { return dispatch(actionCreator.apply(this, args)); }; }
|
中间件
redux 中间件主要用于增强 dispatch 函数,通过原本的 dispatch 函数我们只能够更改数据无法做更多的事情。
redux 中间件的基本原理,是更改仓库中的 dispatch 函数。
中间件本身是一个函数,该函数接收一个仅包含 getState,dispatch 的简易 store 对象,该函数返回一个 dispatch 创建函数,dispatch 创建函数返回一个 dispatch 函数。
简单来说写一个中间件就是这样写:
1 2 3
| const middleware = (store) => (next) => (action) => { };
|
使用中间件的方式:
createStore(reducer, [defaultState, applyMiddleware(middleware1, ....)])
applyMiddleware(middleware1, ....)(createStore)(reducer, defaultState)
洋葱模型

中间件本身是一个函数,该函数接收一个简易版 store 参数,并返回一个 dispatch 创建函数。
dispatch 创建函数接收一个参数 next (下一个中间件经过 dispatch 创建函数返回的 dispatch 函数)
- 返回的
dispatch 函数只接收一个 action 参数
- 中间件函数参数中的
dispatch 函数是仓库中最终的 dispatch 函数
- 第一个中间件返回的
dispatch 最后会成为 store 最终的 dispatch函数,(...args) => dispatch(..args)
也就是这样一张图:

applyMiddleware
无论是使用哪种方式使用中间件,实际依赖的都是 applyMiddleware 这个函数。
compose
函数组合,将一个数组中的函数进行组合,形成一个新的函数,该函数调用时,实际上是反向调用之前组合的函数。这是一个函数式编程中的思想。
实现一个compose函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function compose(...funcs) { if (funcs.length === 0) { return (args) => args; } else if (funcs.length === 1) { return funcs[0]; } return function (...args) { let lastReturn = null; for (let i = funcs.length - 1; i >= 0; i--) { const func = funcs[i]; if (i === funcs.length - 1) { lastReturn = func(...args); } else { lastReturn = func(lastReturn); } } return lastReturn; }; }
|
可以看到核心的逻辑就是在函数数量大于 1 的时候,利用 js 的数组中的方法,可以更加简洁的实现该功能:
1 2 3 4 5
| funcs.reduce( (a, b) => (...args) => a(b(...args)) );
|
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function applyMiddleware(...middlewares) { return (createStore) => (reducer, defaultState) => { const store = createStore(reducer, defaultState); let dispatch = () => { throw new Error("目前还不能使用 dispatch"); }; const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args), }; const chain = middlewares.map((middleware) => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch); return { ...store, dispatch, }; }; }
|
可以发现一个很巧妙的点就是 middlewareAPI 这里,利用闭包巧妙地实现了 dispatch 的替换。