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
| 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
| 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
| funcs.reduce((a, b) => (...args) => a(b(...args)));
|
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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
的替换。