Redux核心概念
- 单一 store ,只能挺过getState()获取状态,不能直接修改
- 只能通过触发 action 修改状态
- 使用纯函数 reducers 描述action如何改变state
整个redux的实现就是围绕上面的这三点进行实现的,整个源码量不大,理解了核心概念后去看源码会发现源码的结构逻辑非常清晰,代码中的注释也非常全面,本篇文章会按照如何实现核心概念的方式去阅读源码。(默认你已经会使用redux了)
Reducer
我们首先来看 reducer 在 redux中的使用来更加深入理解reducer的作用
currentState = currentReducer(currentState, action)
这是 redux 中唯一使用到 reducer 的地方,在dispatch函数中,将旧的state和dispatch的action传入得到新的state,这和redux对于reducer的描述是一模一样的,所以我们在编写 reducer 函数的时候一定要严格遵循redux的规范写成纯函数,这样可以保证你的state变化可以被方便的追踪到。
Action
我们所有的action都要通过 dispatch 所以我们直接来看dispatch的实现,比较有意思的就是redux内其实是有一把同步锁 isDispatching ,这样的做法避免掉了很多处理并发的逻辑,例如并发会带来读脏数据的风险,在看源码之前是根本不知道redux有这样的一个设计的。
function dispatch(action) {
// 省略了一些错误aciton的检测代码 总结下来就是aciton一定是对象并且有type属性
// isDispatching 类似于同步锁,保证每次修改state的原子性
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
//获取新的state
currentState = currentReducer(currentState, action)
} finally {
//开锁
isDispatching = false
}
//通知订阅的函数
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
Store
redux最重要的部分就是createStore,他将存储state,把reducer和action组合在一起。
// 接收 reducer 初始state applyMiddleware添加中间件后返回的函数
export default function createStore(reducer, preloadedState, enhancer) {
// 解决我们不传 preloadedState 第二个参数是 enhancer 的情况
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 在 applyMiddleware 中enhancer做的事情就是添加中间件增强dispatch的能力
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
//store中的转态
let currentReducer = reducer
//我们的state树
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
...
//返回 store对象以及api
return {
//触发aciton使用
dispatch,
//添加state改变的回调使用
subscribe,
//获取 state
getState,
//替换 reducer
replaceReducer,
[$$observable]: observable
}
总结
整个redux源码看下来大概只需要一个小时,但是整体的收获还是非常大的,我们可以看到redux作者是从核心概念入手,一点点将其转化为工具的。