redux
的源码虽然代码量并很少(除去注释大概300行吧)。可是,由于函数式编程的思想在里面体现得淋漓尽致,理解起来并不太容易,因此准备使用三篇文章来分析。html
redux
的核心思想和实现,并用100多行的代码实现了其核心功能,相信看完以后,你会彻底理解 redux
的核心。这里甩掉 combindReducers
和 applyMiddleware
,不会涉及很高深的柯里化、高阶、归并的思想,可是须要你对闭包有必定的理解。其实,redux
源码自己并不可怕,可怕的是网上太多文章把他和函数式放在一块儿来分析(装逼)了!!!吓得咱们一看到就想跑了。redux
的核心以后, 咱们会分析 reducers
合并(即 combindReducers
)的实现。applyMiddleware
)的实现,这是最体现函数式风格的地方,并实现一个处理异步请求的 promise
中间件。在解读 redux
源码以前,咱们首先要弄清楚一个问题,就是 redux
和 react-redux
不是同一个东东。 react-redux
是为 react
而定制的,主要是提供 Provider
组件和 connect
方法,方便于咱们把 redux
和 react组件
绑定起来。可是, redux
是没有限制说必定要跟 react
一块儿使用的。本文只介绍 redux
,不涉及 react
或者 react-redux
。由于我以为,若是把 redux
和 react
放在一块儿讨论,反而会加深了理解的复杂度,分散了咱们的注意力,从而影响咱们分析源码进度。如今要分析 redux
源码,那就只专一于 redux
,甩开 react
, 就连后面的测试例子,也不要引入 react
,就简单的使用原生html和js测试一下就OK了。react
什么是 redux
呢?, 这里也不展开介绍了。就简单的回顾一下 redux
的具体用法:git
reducer
函数redux.createStore(reducer)
方法建立 store
实例store.subscribe(callback)
方法订阅回调事件(即状态变化时会触发回调函数callback)store.dispatch(action)
, 改变 store
的状态可能用些朋友会说,我历来没有用过 store.subscribe
啊,那是由于你使用了 react-redux
, 在 connet()
的时候帮你作了这一步。好吧,说好了不扯 react
的。那下面咱们就就一步步的来实现 redux
的核心功能吧。github
首先来看一下 createStore
, 咱们平时的用法以下:编程
const store = createStore(reducer, preloadedState, enhance)
能够接受3个参数,第一个是自定义的reducer
函数, 第二个是初始状态,第三个是加强器(即 applyMiddleware()
返回的东西),由于前面已经说过了,这里咱们不会涉及到 applyMiddleware
,因此,咱们的 createStore
只接收2个参数,以下:redux
function createStore(reducer, preloadedState) { if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } // 定义一些变量,后面几乎全部的方法都会用到,这就是闭包的力量! let currentState = preloadedState // state let listeners = [] // 订阅事件列表 let isDispatching = false // 是否正在执行reducer }
createStore
参数和可能会用到的变量定义好了,咱们须要实现三个函数,分别是 store.getState
、store.subscribe
和 store.dispatch
。数组
首先来实现 store.getState
方法,这个方法没有好说的,就是把闭包里面的 currentState
返回出去就好了,代码以下:promise
function createStore(reducer, preloadedState) { // 省略和上面重复的代码 // 获取state function getState() { // 若是正在执行reducer,则抛出异常 if (isDispatching) { throw new Error('You may not call store.getState() while the reducer is executing. ') } return currentState; } }
接着咱们来实现 store.subscribe
。这个方法是用来添加订阅回调函数的。首先要判断传进来的参数是否是函数类型,而后,把他它push到回调队列(数组)里面。由于可能后面须要把这个回调取消掉,因此还要返回一个方法给外部调用取消,实现代码以下:闭包
function createStore(reducer, preloadedState) { // 省略和上面重复的代码 // 添加订阅事件 function subscribe(listener) { if(typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } let isSubscribed = true; listeners.push(listener); // 返回一个取消订阅事件的函数 return function unsubscribe() { if(!isSubscribed) { return; } if(isDispatching) { throw new Error('You may not unsubscribe from a store listener while the reducer is executing. '); } isSubscribed = false; const index = listeners.indexOf(listener); listeners.splice(index, 1); } } }
最后,咱们再来看一下 store.dispatch
方法的实现。 dispatch
接受的参数类型是一个 action
。咱们来回顾一下 action
是什么鬼?他要求是一个原生对象,并且必需要有 type
属性,还有可能有 payload
属性。以下是咱们的一个用法 :app
store.dispatch({ type: 'ADD_SHOPPING', payload: 1 })
调用store.dispatch(action)
, 它的返回值也是 action
。下面代码是 store.dispatch()
的实现:
function createStore(reducer, preloadedState) { // 省略和上面重复的代码 function dispatch(action) { // 若是action不是原生对象,则抛出异常 // 由于咱们期待的action结构为"{type: 'xxx', payload: 'xxx'}"的原生对象 if(Object.prototype.toString.call(action, null) !== '[object Object]') { throw new Error('Actions must be plain objects. '); } if(typeof action.type === 'undefined') { throw new Error('Actions may not have an undefined "type" property. ') } if(isDispatching) { throw new Error('Reducers may not dispatch actions.') } // 开始调用reducer获取新状态。由于可能会出错须要用try-catch // 而且无论成功失败,执行完毕后都要设置isDispatching=true try { isDispatching = true; currentState = reducer(currentState, action); } finally { isDispatching = false; } // 遍历全部经过store.subscribe()绑定的的订阅事件,并调用他们 listeners.forEach((listener) => { listener(); }) return action; } }
关于 redux
的分析就写到这里的了。下面是前面分析的代码整合到了一块儿。
function createStore(reducer, preloadedState) { if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } let currentState = preloadedState // state let listeners = [] // 订阅事件列表 let isDispatching = false // 是否正在执行reducer function getState() { // 若是正在执行reducer,则抛出异常 if (isDispatching) { throw new Error('You may not call store.getState() while the reducer is executing. ') } return currentState; } // 添加订阅事件 function subscribe(listener) { if(typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } let isSubscribed = true; listeners.push(listener); // 返回一个取消订阅事件的函数 return function unsubscribe() { if(!isSubscribed) { return; } if(isDispatching) { throw new Error('You may not unsubscribe from a store listener while the reducer is executing. '); } isSubscribed = false; const index = listeners.indexOf(listener); listeners.splice(index, 1); } } function dispatch(action) { // 若是action不是原生对象,则抛出异常 // 由于咱们期待的action结构为"{type: 'xxx', payload: 'xxx'}"的原生对象 if(!isPlainObject(action)) { throw new Error('Actions must be plain objects. '); } if(typeof action.type === 'undefined') { throw new Error('Actions may not have an undefined "type" property. ') } if(isDispatching) { throw new Error('Reducers may not dispatch actions.') } // 开始调用reducer获取新状态。由于可能会出错须要用try-catch // 而且无论成功失败,执行完毕后都要设置isDispatching=true try { isDispatching = true; currentState = reducer(currentState, action); } finally { isDispatching = false; } // 遍历全部经过store.subscribe()绑定的的订阅事件,并调用他们 listeners.forEach((listener) => { listener(); }) return action; } // 将getState, subscribe, dispatch这三个方法暴露出去 // 建立了store实例以后,能够store.getState()、store.subscripbe()... return { getState, subscribe, dispatch } }
完整的代码和测试例子,能够到个人github下载 点击进入simplest-redux 。若是以为我分析得还不是太清楚的,建议把github上的代码clone下来,本身多看几遍,并在demo中运行调试几下就会明白的了。