最近公司有个项目使用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,其实就是一个回调函数

 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帮我们构建数据结构,并且演示如何做多级的数据结构

通过Redux源码学习基础概念一:简单例子入门的更多相关文章

  1. Redux源码学习笔记

    https://github.com/reduxjs/redux 版本 4.0.0 先了解一下redux是怎么用的,此处摘抄自阮一峰老师的<Redux 入门教程> // Web 应用是一个 ...

  2. redux源码学习笔记 - createStore

    本篇是学习redux源码的一些记录,学习的redux版本是^4.0.1. 在页面开发时,需要管理很多状态(state),比如服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱.r ...

  3. redux源码学习笔记 - applyMiddleware

    在创建store时,createStore(reducer, preloadedState, enhancer),除了reducer函数,初始状态,还可以传入enhancer.这个enhancer在c ...

  4. redux源码学习笔记 - combineReducers

    上一篇有了解到,reducer函数的两个为:当前state和此次dispatch的action. state的结构是JavaScript对象,每个key都可以代表着不同意义的数据.比如说 { list ...

  5. Vue2.x源码学习笔记-从一个小例子查看vm实例生命周期

    学习任何一门框架,都不可能一股脑儿的从入口代码从上到下,把代码看完, 这样其实是很枯燥的,我想也很少有人这么干,或者这么干着干着可能干不下去了. 因为肯定很无聊. 我们先从一个最最简单的小例子,来查看 ...

  6. 走进Spring Boot源码学习之路和浅谈入门

    Spring Boot浅聊入门 **本人博客网站 **IT小神 www.itxiaoshen.com Spring Boot官网地址:https://spring.io/projects/spring ...

  7. Redux源码分析之基本概念

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  8. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  9. 【 js 基础 】【 源码学习 】柯里化和箭头函数

    最近在看 redux 的源码,代码结构很简单,主要就是6个文件,其中 index.js 负责将剩余5个文件中定义的方法 export 出来,其他5个文件各自负责一个方法的实现. 大部分代码比较简单,很 ...

随机推荐

  1. 通过html和css做出下拉导航栏的效果

    通过观察了百度的首页,对于更多产品一栏,觉得可以不涉及JS便可写出下拉导航栏的效果 1.先设计出大体的框架 <div class="nav"> <ul> & ...

  2. 挡不住的好奇心:ASP.NET 5是如何通过XRE实现跨平台的

    .NET程序员也有自己的幸福,.NET的跨平台是一种幸福,.NET的开源也是一种幸福,而更幸福的是可以通过开源的.NET了解.NET是如何一步步走向跨平台的,所以幸福是一种过程. 在.NET跨平台的进 ...

  3. Python黑帽编程 3.3 MAC洪水攻击

    Python灰帽编程 3.3 MAC洪水 传统的交换机(我只对我目前使用的交互机做过测试,按照常识只能这样表述)在数据转发过程中依靠对CAM表的查询来确定正确的转发接口,一旦在查询过程中无法找到相关目 ...

  4. [.net 面向对象程序设计深入](4)MVC 6 —— 谈谈MVC的版本变迁及新版本6.0发展方向

    [.net 面向对象程序设计深入](4)MVC 6 ——谈谈MVC的版本变迁及新版本6.0发展方向 1.关于MVC 在本篇中不再详细介绍MVC的基础概念,这些东西百度要比我写的全面多了,MVC从1.0 ...

  5. MapReduce剖析笔记之一:从WordCount理解MapReduce的几个阶段

    WordCount是一个入门的MapReduce程序(从src\examples\org\apache\hadoop\examples粘贴过来的): package org.apache.hadoop ...

  6. C#设计模式之命令

    IronMan之命令 在本篇中还是围绕着“IronMan”来讲,在上一篇“外观”中我们说到过“控制中心”.它是负责IronMan的核心,所有能想象到的功能都跟它有关系,就在使用它的时候,发现了一些问题 ...

  7. 修改TNSLSNR的端口

    oracle 服务一启动 TNSLSNR.exe 会占用8080端口,这时,如果我们其他程序需要使用8080端口就会比较麻烦,所以需要改一下端口: 用dba账户登录 CMD>sqlplus sy ...

  8. TDR分辨率

    在日常的生活工作中,有很多测试测量的工具,比如测量长度的尺子,计量时间的钟表等等,谈到测试测量工具的时候,分辨率是关键指标之一,比如尺子的 分辨率是1mm,时钟的分辨率是秒.所谓分辨率就是测试测量工具 ...

  9. SQL Server DDL触发器运用

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 基础知识(Rudimentary Knowledge) DDL运用场景(DDL Scene) ...

  10. pipedata3d User Guide

    pipedata3d User Guide 1. Introduction 在管道设计过程中,会使用到大量的标准,如ASME,DIN,GB,CB,HG,SH等等.管道设计人员在设计过程中,需要翻阅相关 ...