通过Redux源码学习基础概念一:简单例子入门
2016-09-30 14:56
666 查看
最近公司有个项目使用react+redux来做前端部分的实现,正好有机会学习一下redux,也和小伙伴们分享一下学习的经验。
首先声明一下,这篇文章讲的是Redux的基本概念和实现,不包括react-redux。
源码地址:https://github.com/lyc-chengzi/reactProject
首先说一下我理解的Redux:
它只是一个管理数据的一个工具,帮助我们创建app中唯一的一个数据结构的树,并且按照约定的方法来管理这颗树,让我们的数据的更改变为可预测的。
任何一个普通的框架,或者如angular, jquery等都可以依赖于这个数据结构,来做自己的事情。
react-redux:
这个库则是帮助我们将react组件和redux创建的数据树串联起来,让普通的react组件根据state来重新渲染。
以后可能也会有angular-redux, jquery-redux等等库,帮助我们实现其他框架的ui渲染。
好了,下面进入正题:
Redux的运行原理
1. 先定义好我们的reducer -> 2. 组装reducer -> 3. 调用redux.createStore创建一个store -> 4. store调用dispatch方法 ->5. 触发你写的reducer -> 6. 返回新的state
举一个简单的例子,我们的app就是一个计数器,实现加和减的功能,一个最简单的数据结构:{counter: 0};下面开始按照上面的步骤实现
1. 先定义一个我们的reducer,其实就是一个回调函数
reducer固定会接收两个参数,state和action。
reducer的作用就是接受一个旧的state,然后内部加工处理后,返回一个新的state给redux,这就是reducer的职能:扩展或修改原有state并返回!
第一个参数state就是redux告诉我们的更改前的数据,我们以此为基础做一些操作。具体是那些操作,就通过第二个参数action告诉我们。
如上面的代码,通过action.type,我们处理了counter_add action,即数字加1操作,我们把state+1;其他未知操作我们直接返回原有state。
这样一个最简单的reducer就创建完了,是不是很简单? 就是一个普通的回调函数
2. 组装reducer
这一步的目的是返回一个根reducer,因为默认state为undefined,所以我们给state一个默认值{}。根reducer返回一个json对象,key为名称,value为具体的实现reducer
3. 创建store
简单的2行代码,通过我们定义的根reducer,redux创建一个store对象返回给我们。
我们只能通过dispatch方法来改变整个app的state,调用getState方法查看初始化后的数据结构
4. 调用dispatch,来实现计数器增加
dispatch方法只接受一个action参数。
action为一个json对象:必须包含type属性,用来标识是哪一个action,也可以有其他属性作为附加值传递到reducer
这里我们传递了'counter_add'告诉redux。
这个action会从你的根reducer一直传递下去,到末级reducer。只要我们定义的reducer处理这个action,就会更新state。
然后我们打印最新的state,如下
如果我们要更新state,只能通过调用store.dispatch方法,传递action参数。然后redux会调用我们的reducer来处理这个action,最后return 最新的state。
下面我们通过源码来看一下关键的两个函数是如何运行的。
1. createStore
上面是createStore的关键代码。
使用了闭包的技巧,隐藏了几个关键变量:
currentReducer=>我们传入的根reducer
currentState => 当前默认state,我们默认为一个空json对象{}
nextListeners和currentListeners用来保存监听函数,当我们调用dispatch方法时会触发
isDispatching => 当前调度状态,只有当前调度状态是false时才会执行dispatch方法
初始化完几个关键内部变量后,执行了一次默认的dispatch方法,action.type为reduxInit
最后返回了一个包装对象,包含了对外公开的方法。我们只能通过这几个方法来操作内部的变量。
(虽然可以var state= store.getState();获取state之后直接修改,但千万不要这么做,不然redux也没有意义了。个人认为如果getState()返回一个clone的currentState会更好)
2.我们来看一下dispatch都干了些什么
非常简单,只是调用了你根reducer函数,然后将内部保存的当前state,和action传了过去,剩下的都是你的reducer干的事情了。
所以createStore默认调用了一次dispatch,action.type为init,我们的reducer没有对应的处理方法,直接将默认的state返回了回去。
现在也就明白了为什么我们的reducer为什么要在default的时候返回变化前的state。
所以总结一下redux,就是dispatch的过程,(因为createStore也是dispatch,不过是在内部调用的),每一次dispatch都会调用一次我们的根reducer,然后重新构建一遍数据,
然后把新的数据保存起来。
到此我们就把一个最简单的redux例子学完了。下一篇将会介绍另一种组装reducer的方法:通过调用
方法让redux帮我们构建数据结构,并且演示如何做多级的数据结构
首先声明一下,这篇文章讲的是Redux的基本概念和实现,不包括react-redux。
源码地址:https://github.com/lyc-chengzi/reactProject
首先说一下我理解的Redux:
它只是一个管理数据的一个工具,帮助我们创建app中唯一的一个数据结构的树,并且按照约定的方法来管理这颗树,让我们的数据的更改变为可预测的。
任何一个普通的框架,或者如angular, jquery等都可以依赖于这个数据结构,来做自己的事情。
react-redux:
这个库则是帮助我们将react组件和redux创建的数据树串联起来,让普通的react组件根据state来重新渲染。
以后可能也会有angular-redux, jquery-redux等等库,帮助我们实现其他框架的ui渲染。
好了,下面进入正题:
Redux的运行原理
1. 先定义好我们的reducer -> 2. 组装reducer -> 3. 调用redux.createStore创建一个store -> 4. store调用dispatch方法 ->5. 触发你写的reducer -> 6. 返回新的state
举一个简单的例子,我们的app就是一个计数器,实现加和减的功能,一个最简单的数据结构:{counter: 0};下面开始按照上面的步骤实现
1. 先定义一个我们的reducer,其实就是一个回调函数
function counter(state = 0, action){ switch (action.type){ case 'counter_add': return ++state; break; default: return state; } }
reducer固定会接收两个参数,state和action。
reducer的作用就是接受一个旧的state,然后内部加工处理后,返回一个新的state给redux,这就是reducer的职能:扩展或修改原有state并返回!
第一个参数state就是redux告诉我们的更改前的数据,我们以此为基础做一些操作。具体是那些操作,就通过第二个参数action告诉我们。
如上面的代码,通过action.type,我们处理了counter_add action,即数字加1操作,我们把state+1;其他未知操作我们直接返回原有state。
这样一个最简单的reducer就创建完了,是不是很简单? 就是一个普通的回调函数
2. 组装reducer
var app = function(state = {}, action){ return {counter: counter(state.counter, action)}; };
这一步的目的是返回一个根reducer,因为默认state为undefined,所以我们给state一个默认值{}。根reducer返回一个json对象,key为名称,value为具体的实现reducer
3. 创建store
let store = redux.createStore(app); console.log(store.getState());
简单的2行代码,通过我们定义的根reducer,redux创建一个store对象返回给我们。
我们只能通过dispatch方法来改变整个app的state,调用getState方法查看初始化后的数据结构
4. 调用dispatch,来实现计数器增加
store.dispatch({type: 'counter_add'}); console.log(store.getState());
dispatch方法只接受一个action参数。
action为一个json对象:必须包含type属性,用来标识是哪一个action,也可以有其他属性作为附加值传递到reducer
这里我们传递了'counter_add'告诉redux。
这个action会从你的根reducer一直传递下去,到末级reducer。只要我们定义的reducer处理这个action,就会更新state。
然后我们打印最新的state,如下
如果我们要更新state,只能通过调用store.dispatch方法,传递action参数。然后redux会调用我们的reducer来处理这个action,最后return 最新的state。
下面我们通过源码来看一下关键的两个函数是如何运行的。
1. createStore
function createStore(reducer, preloadedState, enhancer) { var currentReducer = reducer; var currentState = preloadedState; var currentListeners = []; var nextListeners = currentListeners; var isDispatching = false; dispatch({ type: ActionTypes.INIT }); return _ref2 = { dispatch: dispatch, subscribe: subscribe, getState: getState, replaceReducer: replaceReducer }, _ref2[_symbolObservable2['default']] = observable, _ref2; }
上面是createStore的关键代码。
使用了闭包的技巧,隐藏了几个关键变量:
currentReducer=>我们传入的根reducer
currentState => 当前默认state,我们默认为一个空json对象{}
nextListeners和currentListeners用来保存监听函数,当我们调用dispatch方法时会触发
isDispatching => 当前调度状态,只有当前调度状态是false时才会执行dispatch方法
初始化完几个关键内部变量后,执行了一次默认的dispatch方法,action.type为reduxInit
最后返回了一个包装对象,包含了对外公开的方法。我们只能通过这几个方法来操作内部的变量。
(虽然可以var state= store.getState();获取state之后直接修改,但千万不要这么做,不然redux也没有意义了。个人认为如果getState()返回一个clone的currentState会更好)
2.我们来看一下dispatch都干了些什么
function dispatch(action) { if (!(0, _isPlainObject2['default'])(action)) { throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); } if (typeof action.type === 'undefined') { throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?'); } if (isDispatching) { throw new Error('Reducers may not dispatch actions.'); } try { isDispatching = true; currentState = currentReducer(currentState, action); } finally { isDispatching = false; } var listeners = currentListeners = nextListeners; for (var i = 0; i < listeners.length; i++) { listeners[i](); } return action; }
非常简单,只是调用了你根reducer函数,然后将内部保存的当前state,和action传了过去,剩下的都是你的reducer干的事情了。
所以createStore默认调用了一次dispatch,action.type为init,我们的reducer没有对应的处理方法,直接将默认的state返回了回去。
现在也就明白了为什么我们的reducer为什么要在default的时候返回变化前的state。
所以总结一下redux,就是dispatch的过程,(因为createStore也是dispatch,不过是在内部调用的),每一次dispatch都会调用一次我们的根reducer,然后重新构建一遍数据,
然后把新的数据保存起来。
到此我们就把一个最简单的redux例子学完了。下一篇将会介绍另一种组装reducer的方法:通过调用
redux.combineReducers
方法让redux帮我们构建数据结构,并且演示如何做多级的数据结构
相关文章推荐
- SAP ABAP/4学习--学习使用OO在ABAP中.简单入门概念.用个例子来说明
- SAP ABAP/4学习--学习使用OO在ABAP中.简单入门概念.用个例子来说明
- linux设备驱动开发范例,linux驱动例子,Linux设备驱动详解范例example 宋宝华版的光盘驱动源码,实测编译通过入门学习用
- SAP ABAP/4学习--学习使用OO在ABAP中.简单入门概念.用个例子来说明
- SVM实现多分类的程序基础工作(二)——通过一个简单libsvm例子迈入libsvm学习的大门
- mybatis学习-入门(4)-一个完整的web例子,通过json将字符串传递给前台,然后显示出来
- Android中关于JNI 的学习(零)简单的例子,简单地入门
- 通过一个简单的例子学习编译器是如何执行程序的
- 最简单,最适合入门学习的三层架构例子
- Hadoop基础入门学习笔记(基本概念)
- Vxworks嵌入式开发系统入门基础概念学习
- Android 简单例子以及入门学习资料链接
- Swift教程_通过改造官方Sample学习Swift(二)_Swift基础(简单值、控制流、方法和闭包)
- 简单的XML结合XSL的例子,有助于学习内部的原理(内附源码)
- ant + junit 基础学习,一个简单易懂的例子
- 深入学习Django源码基础9 - 简单分析DjangoORM部分
- Mybatis 入门学习,简单例子
- ASP.NET 学习历程 - 清清月儿 .NET万花筒 Asp.net技术 Asp.net教程 Asp.net源码 Asp.net基础 Asp.net控件 Asp.net入门 - CSDNBlog
- 通过例子学习Lua(5) ---- Lua与C交互入门
- 通过例子学习Lua(5) ---- Lua与C交互入门 (转)