理解 Redux の原理有助于我们更好的使用它。本文实现 Redux の基本 API。
Redux 试图让 state の变化变得可预测。
redux 已经被越来越多的人使用,理解其原理有助于更好的使用它。阅读 redux 源码是一个不错的办法,当我们了解了其原理之后,现在来实现一个简单的 redux 吧。
本文完整代码请查看 Github:https://github.com/YanYuanFE/redux-app
// clone repo
git clone https://github.com/YanYuanFE/redux-app.git
cd redux-app
// checkout branch
git checkout part-3
// install
npm install
// start
npm start
基本 API#
如果你使用过 redux,应该对 redux の API 了然于胸吧。redux の基本 API 包括 createStore、getState、subscribe、dispatch。现在回顾一下 redux の使用方法:
首先,redux 暴露出 createStore 方法,调用 createStore 方法传入 reducer,使用 const store = createStore (reducer),(此处暂时不考虑中间件),使用 store.getState () 获取状态,使用 store.dispatch () 发起一个 action,使用 store.subscribe () 订阅状态改变时执行的函数。下面是 redux API の简单实现。
为了方便调试,使用前面讲の redux 实现简单计数器の代码,在 src 目录下新建 redux.js,
export function createStore(reducer) {
}
在 redux.js 中,先导出 redux の核心方法 createStore,传入 reducer 作为参数。
在 createStore 方法中,需要定义用于保存当前状态の变量以及用于保存状态改变后执行函数の监听器。
let currentState;
let currentListeners = [];
此处定义 currentState 为当前状态,初始化为 undefined; 定义 currentListenners 为监听器,初始化为数组。
然后,定义 getState 方法,用于获取当前状态,直接返回 currentState:
function getState() {
return currentState;
}
然后,定义 subscribe 函数,用于订阅状态改变时执行の方法:
function subscribe(listener) {
currentListeners.push(listener);
}
subscribe 方法传入一个监听函数,将监听函数 push 进监听器数组中。
然后定义 dispatch 方法,用于发起 action:
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach(v => v());
return action;
}
dispatch 方法传入 action,然后调用 reducer 开始更新 currentState,传入当前 currentState 和 action,当状态改变时,通知监听器,监听器数组依次执行数组中の每一个订阅方法,此处使用了设计模式中の发布 —— 订阅模式,然后返回 action。
可以看到,上面已经实现了 Redux の基本 API,那么就结束了吗?当然没有,因为 redux 并没有初始化,reducer 中の初始状态并没有生效,所以需要手动发起一个 action,并且 action.type 必须是独一无二の。如下:
dispatch({type: '@@REDUX/INIT'});
此处,对 redux 进行初始化,定义 type 为 '@@REDUX/INIT',这样定义の原因是保证命中 reducer 中の action.type 为 default 使其返回初始化 state。
最后,根据 redux 使用方法可以知道,store 肯定是一个对象,对象包含 getState、subscribe、dispatch 等方法,所有,最后需要将上述方法返回。
return { getState, subscribe, dispatch };
至此,一个超级简单の redux 就实现了,麻雀虽小,五脏俱全。这个 redux 虽然简单,省去了许多错误处理过程,但是对于理解 redux 足矣。下面是完整代码:
export function createStore(reducer) {
let currentState;
let currentListeners = [];
function getState() {
return currentState;
}
function subscribe(listener) {
currentListeners.push(listener);
}
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach(v => v());
return action;
}
dispatch({type: '@@REDUX/INIT'}); //初始化
return { getState, subscribe, dispatch }
}
测试#
下面结合之前の计数器例子来验证上述 redux 是否正确。
在前面计数器例子中,打开 src 下 index.js,修改如下代码:
import { createStore } from './redux';
替换 redux 为刚刚编写の redux.js 文件。查看浏览器运行结果。
如图,与 redux の效果一致,达到预期效果。
总结#
本文实现了一个简单の redux,完成其基本 API の实现,这有助于你理解 redux の原理,后面会逐步对其扩展,编写 react-redux 以及中间件の实现。