Redux
是当今比较流行的状态管理库,它不依赖于任何的框架,而且配合着react-redux
的使用,Redux
在不少公司的React项目中起到了举足轻重的做用。接下来笔者就从源码中探寻Redux是如何实现的。javascript
注意:本文不去过多的讲解Redux的使用方法,更多的使用方法和最佳实践请移步Redux官网。java
随着咱们项目的复杂,项目中的状态就变得难以维护起来,这些状态在何时,处于什么缘由,怎样变化的咱们就很难去控制。所以咱们考虑在项目中引入诸如Redux、Mobx这样的状态管理工具。react
Redux其实很简单,能够简单理解为一个约束了特定规则而且包括了一些特殊概念的的发布订阅器。redux
在Redux中,咱们用一个store来管理一个一个的state。当咱们想要去修改一个state的时候,咱们须要去发起一个action,这个action告诉Redux发生了哪一个动做,可是action不可以去直接修改store里头的state,他须要借助reducer来描述这个行为,reducer接受state和action,来返回新的state。数组
在Redux中有三大原则:闭包
抛去一些项目的配置文件和其余,Redux的源码其实不多很简单:app
首先咱们先经过createStore函数的入参和返回值来简要理解它的功能:框架
export default function createStore(reducer, preloadedState, enhancer) {
// ...
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
复制代码
createStore接受三个参数:异步
applyMiddleware
;createStore返回一个对象,对象中包含使用store的基本函数:函数
接下来咱们来看看createStore的核心逻辑,这里我省略了一些简单的警告和判断逻辑:
export default function createStore(reducer, preloadedState, enhancer) {
// 判断是否是传入了过多的enhancer
// ...
// 若是不传入preloadedState只传入enhancer能够写成,const store = createStore(reducers, enhancer)
// ...
// 经过在加强器传入createStore来加强store的基本功能,其余传入的参数做为返回的高阶函数参数传入;
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
// 闭包内的变量;
// state做为内部变量不对外暴露,保持“只读”性,仅经过reducer去修改
let currentReducer = reducer
let currentState = preloadedState
// 确保咱们所操做的listener列表不是原始的listener列表,仅是他的一个副本;
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
// 确保咱们所操做的listener列表不是原始的listener列表,仅是他的一个副本;
// 只有在dispatch的时候,才会去将currentListeners和nextListeners更新成一个;
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
// 经过闭包返回了state,state仅能够经过此方法访问;
function getState() {
// 判断当前是否在dispatch过程当中
// ...
return currentState
}
// Redux内部的发布订阅器
function subscribe(listener) {
// 判断listener的合法性
// ...
// 判断当前是否在dispatch过程当中
// ...
let isSubscribed = true
// 复制一份当前的listener副本
// 操做的都是副本而不是源数据
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
// 判断当前是否在dispatch过程当中
// ...
isSubscribed = false
ensureCanMutateNextListeners()
// 根据当前listener的索引从listener数组中删除来实现取掉订阅;
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
function dispatch(action) {
// 判断action是否是一个普通对象;
// ...
// 判断action的type是否合法
// ...
// 判断当前是否在dispatch过程当中
// ...
try {
isDispatching = true
// 根据要触发的action, 经过reducer来更新当前的state;
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 通知listener执行对应的操做;
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
// 替换reducer,修改state变化的逻辑
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
// 此操做对ActionTypes.INIT具备相似的效果。
// 新旧rootReducer中存在的任何reducer都将收到先前的状态。
// 这有效地使用来自旧状态树的任何相关数据填充新状态树。
dispatch({ type: ActionTypes.REPLACE })
}
function observable() {
const outerSubscribe = subscribe
return {
// 任何对象均可以被用做observer,observer对象应该有一个next方法
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
}
function observeState() {
if (observer.next) {
observer.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
// 返回一个带有unsubscribe方法的对象能够被用来在store中取消订阅
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
// 建立store时,将调度“INIT”操做,以便每一个reducer返回其初始状态,以便state的初始化。
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
复制代码
仅靠上面的createStore其实已经能够完成一个简单的状态管理了,可是随着业务体量的增大,state、action、reducer也会随之增大,咱们不可能把全部的东西都塞到一个reducer里,最好是划分红不一样的reducer来处理不一样模块的业务。
可是也不能建立多个store维护各自的reducer,这就违背了Redux的单一store原则。为此,Redux提供了combineReducers让咱们将按照业务模块划分的reducer合成一个rootReducer。
接下来咱们看看combineReducers的源码,这里也是去掉了一些错误警告的代码和一些错误处理方法:
export default function combineReducers(reducers) {
// 取出全部的reducer遍历合并到一个对象中
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
// 判断未匹配的refucer
// ...
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
// 错误处理的一些逻辑
// ...
return function combination(state = {}, action) {
// 错误处理的一些逻辑
// ...
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
// 对应的reducer
const reducer = finalReducers[key]
// 根据指定的reducer找到对应的state
const previousStateForKey = state[key]
// 执行reducer, 返回当前state
const nextStateForKey = reducer(previousStateForKey, action)
// nextStateForKey undefined的一些判断
// ...
// 整合每个reducer对应的state
nextState[key] = nextStateForKey
// 判断新的state是否是同一引用, 以检验reducer是否是纯函数
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
复制代码
其实到这里能够简单的看出combineReducers就是把多个reducer拉伸展开到到一个对象里,一样也把每个reducer里的state拉伸到一个对象里。
现有的store每一次state的更新都须要手动的dispatch每个action,而咱们其实更须要的是自动的dispatch全部的action。这里就用到了bindActionCreators方法。
如今咱们来看看bindActionCreators的源码
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
// 返回绑定了this的actionCreator
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// actionCreators类型判断的错误处理
// ...
// 为每个actionCreator绑定this
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
复制代码
其实咱们在react项目中对这个方法是几乎无感知的,由于是在react-redux的connect中调用了这个方法来实现自动dispatch action的,否则须要手动去dispatch一个个action。
compose是Redux导出的一个方法,这方法就是利用了函数式的思想对函数进行组合:
// 经过从右到左组合参数函数得到的函数。例如,compose(f, g, h)与do(...args)=> f(g(h(... args)))相同。
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码
咱们的action会出现同步的场景,固然也会出现异步的场景,在这两种场景下dispacth的执行时机是不一样的,在Redux中,可使用middleware来对dispatch进行改造,下面咱们来看看applyMiddleware的实现:
import compose from './compose'
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 经过从右到左组合参数函数得到的函数。例如,compose(f, g, h)与do(...args)=> f(g(h(... args)))相同。
// 对dispatch改造
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
到此,Redux源码的部分就分析完了,可是在具体和React结合的时候还须要用到react-redux,下一篇文章,我将深刻到react-redux的源码学习,来探索,在react中,咱们如何去使用Redux。