学习Redux之分析Redux核心代码分析
1. React,Flux简单介绍
学习React我们知道,React自带View和Controller库,因此,实现过程中不需要其他任何库,也可以独立开发应用。但是,随着应用程序规模的增大,其需要控制的state也会越来越多,为了统一控制,我们一般都会将所有的状态和控制逻辑都放在顶层组件中,这样就会导致,react顶层组件中体积庞大,并且数据和组件混合在一起,非常不方便管理。
在这种情况下Flux,就出现了,他提出了将应用程序,分为三部分:dispatcher, store,view。其中,dispatcher负责分发事件;store负责保存数据,同时响应事件并更新数据;view负责订阅store中的数据,根据数据渲染相应的页面,并提供用户交互。
实际上来说,从React到Flux,我们只是将我们React中定义的顶层组件中数据与逻辑拆分到不同的文件中:
1. 将state写在store中,在store 中存放数据,并向外提供一个getter接口,为state提供唯一数据接口;并创建change事件注册函数;
2. 将合成事件的回调函数,转化成action对象(js对象,具有一个type属性,用于表示action的类型,并通过payload属性传递数据),并使用Dispatcher对象中的dispatch方法将事件对象分发出去。
3. 利用dispatcher对象中提供的register方法,注册action,接收dispatch方法分发action对象,对store的数据进行相应操作,并触发change事件;
4. 我们在组件中监听自定义change事件,当监听到change事件,重新获取store中的数据 ,设置state,重新渲染页面。
实际上Flux并不是一个框架,而是前端应用架构,它提出了单向数据流,可以提供清晰的数据流。同时将数据和组件分离,使应用程序结构更清晰。但是flux依旧存在一些问题。
Redux,利用Flux提出的架构思想和少量的核心代码,方便了我们的开发。以上都是简单介绍,下面通过Redux核心代码,其中部分都是简化代码。
2. Redux核心代码介绍
1. createStore方法
下面代码是createStore的简单实现,其中保留了其中三个主要的方法getState,dispatch,subscribe实现,省略了参数处理部分,和一些其他方法实现。
// createStore方法简单实现
const createStore = (reducer) => {
// 定义state,监听器数组 let state; let listeners = [];
// 定义getter方法,唯一一个获取数据的接口 const getState = () => state;
// 定义dispatch方法,用户分发action事件,接收一个action对象,生成新的state,并通知所有监听器 const dispatch = (action) => {
// 利用reducer生成新的state state = reducer(state, action);
// 通知所有监听器,调用回调函数 listeners.forEach(listener => listener()); };
// 定义subscribe方法,注册监听器,并返回一个用于取消当前注册监听器的函数,用于取消监听
const subscribe = (listener) => {
// 添加到监听数组中 listeners.push(listener);
// 返回取消监听函数 return () => { listeners = listeners.filter(l => l !== listener); } };
// 调用dispatch,初始化state dispatch({});
// 返回getState,dispatch,subscribe三个方法 return { getState, dispatch, subscribe }; };
我们从上面代码可以知道,createStore接收一个reducer函数参数,每次调用dispatch函数,利用reducer函数,以及之前的state,以及action,计算出最新的state,并通知所有的监听器,通过调用getState方法,调用最新的state。同时组件通过subscribe函数,注册一个监听器,从而每当数据更新时,通知监听组件。
以上就是Redux中最主要的createStore对象中主要方法介绍。
2. combineReducers函数
reducer函数,其实就是根据当前的state和action,计算出一个新的state,之所以叫reducer,是因为它的功能就是action对象数组调用reduce函数,产生最新的状态。
当我们的应用程序规模非常大时,我们需要将reducer进行拆分,我们可以直接自定义一个总的合并reducer,也可以使用conbineReducers函数来合并,最终效果等价。
// 合并多个reducer // 返回一个合并的reducer函数,接收两个参数state和action /** * combineReducers调用方法 * combineReducers({ * a: a, * b: b, * c: c * }); * 等价于 * { * a: a(state.a, action), * b: b(state.b, action), * c: c(state.c, action) * } */ const combineReducers = reducers => { return (state = {}, action) => { // 获取所有自身可枚举属性,并进行reduce操作, // 将合并在一起,初始值为空数组 return Object.keys(reduers).reduce( (nextState, key) => { nextState[key] = reducers[key](state[key], action); return nextState; }, {} ); }; };
3. applyMiddleware函数
该函数是在当createStore函数传入applyMiddleware参数时,在createStore中调用的。其实createStore函数的实际参数有三个reducer, preloadedState(state默认值), enhancer, 并且在函数中一些了参数判断,主要就是,将enhancer(在这里是applyMiddleware函数) 参数进行匹配,如果createStore参数中具有enhancer, 则直接调用enhancer(createStore)(reducer, preloadedState)对store进行初始化。
其中applyMiddleware中有调用createStore函数,来初始化store,然后用middleware对初始化的store 中的dispatch函数进行包装,得到最终增强的applyMiddleware,最终返回具有增强的applyMiddleware的store。
// 将中间件和store.dispatch组合 // 返回具有整合后的dispatch方法的store /**/ export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { // 根据上面分析,reducer,preloadedState即为createStore中的reducer,preloadedState // enhancer为空 // 创建一个初始化的store,基本逻辑即为上面createStore函数分析 var store = createStore(reducer, preloadedState, enhancer); // 获取此时dispatch函数副本 var dispatch = store.dispatch; // 用于存放中间件函数数组 var chain = []; // 中间件的执行参数,中间件编写规定 var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; // 对每一个中间件进行处理后存放 chain = middlewares.map(middleware => middleware(middlewareAPI)); // 使用compose函数,将处理后的中间件,和dispatch进行合并,得到增强后的dispatch // 如果chain为[f1, f2, f3],那么dispatch经过compose处理后为 f1(f2(f3(dispatch))) dispatch = compose(...chain)(store.dispatch); // 将增强后的dispatch,赋值给store,并返回store return {...store, dispatch } } }
其中的compose函数,主要就是利用了reduceRight函数对参入的参数进行了处理,就不详细说明了。
我想大家如果了解了上面这些,就能容易理解Redux如何工作的了。