tip:有问题或者需要大厂内推的+我脉脉哦:丛培森 ٩( ‘ω’ )و

【本文源址:http://blog.csdn.net/q1056843325/article/details/54784109 转载请添加该地址】

前几天给大家谈了谈React
不过它只是一个侧重于UI的框架
只能算作是MVC中的V(View视图)
而且只是DOM的一个抽象层,不是Web应用完整解决方案
如果仅仅用它构建大型项目
你会非常的吃力
#简介

14年,Facebook提出Flux架构意图解决这个问题
15年,Dan Abramov将 Flux 与函数式编程相结合,创造了Redux,由于简单易学就开始流行起来
16年,Dan Abramov被facebook挖走了

Redux体积很小,如果删掉源码的空行和注释,连500行代码都不到
别看它小(压缩版7KB),却非常有用

#优点
使用它构建web应用有如下优点:

  • 预测
    始终有一个准确的数据源,就是store, 对于如何将actions以及应用的其他部分和当前的状态同步可以做到绝不混乱。
  • 维护
    具备可预测结果的性质和严格的组织结构让代码更容易维护。
  • 组织
    对代码应该如何组织更加严苛,这使代码更加一致,对团队协作更加容易。
  • 测试
    编写可测试代码的首要准则就是编写可以仅做一件事并且独立的小函数。Redux的代码几乎全部都是这样的函数:短小、纯粹、分离。
  • 服务端渲染
    可以带来更好的用户体验并且有助于搜索引擎优化,尤其是对于首次渲染。仅仅是把服务端创建的store传递给客户端就可以。
  • 开发者工具
    开发者可以实时跟踪在应用中正在发生的一切,从actions到状态的改变。
  • 社区与生态圈
    存在很多支持Redux的社区,使它能够吸引更多的人来使用。

可能有同学会说,这不是和百度百科一样么
这应该不能算抄吧,其实百度百科redux的词条是我建的o( ̄▽ ̄)d
不过编辑的蛮差劲的,希望各位有时间的时候能补充词条让更多人了解

#使用场景
还要强调的一点就是,不是任何时候都需要用它
如果我们用React遇到瓶颈了,或许我们才需要用到Redux
一般情况它都是配合React使用的(因为React用state描述界面,Redux控制state,配合天衣无缝)
不过也可以配合其他框架(甚至原生JavaScript)

阮大神在这方面总结的很详细
这些情况不需要用Redux

  • 用户的使用方式非常简单
  • 用户之间没有协作
  • 不需要与服务器大量交互,也没有使用 WebSocket
  • 视图层(View)只从单一来源获取数据

下列情况建议使用Redux

  • 用户的使用方式复杂
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据

组件角度考虑的话,Redux的使用场景

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

#设计原理
学Redux的时候就觉得很亲切,但不知道亲切在哪儿
后来知道了,这不就是命令模式么
而这个命令command对象就是store

命令模式的应用场景就是:有时需要向某些对象发送请求,但是不知道请求的接收者是谁,
也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,
使得请求发送者和请求接收者能够消除彼此之间的耦合关系

而我们经常会遇到这样的问题
触发事件后,必须向某些负责具体行为的对象发送请求(接受者receiver)
但还不知道接收者是什么,也不到它要做什么,这是我们就需要命令对象command解耦

所以说白了Redux就是一种设计模式——命令模式
(可见设计模式有多重要,有时间真的该深入研究一下)

#设计思想

Redux设计思想就两句话,切记
(再次引用阮大神的总结)

  • Web应用是一个状态机(state machine),视图与状态一一对应
  • 所有状态(state)保存在一个对象中(store)

#核心概念
其实Redux架构并不难
文件小,概念也没有多少
最最重要的就是这个
##单一状态树
Redux中单一状态树这个概念很重要
什么是单一状态树呢?(也叫单一数据源)
所有的状态state对象(数据)都以树的形式储存唯一的store中
页面中的任何变动,首先都要去改变状态树(store),然后以某种形式呈现到页面
还记得刚刚了解的Redux设计思想吗
一个视图对应一个状态对象

举个例子
比如我们的一个state对象

{text: 'a text',color: 'red'
}

它可能展示一个这样的视图

<p style="color: red;">a text<p>

现在状态改变了,变成了这个样子

{text: 'another text',color: 'green'
}

状态改变了,视图也随之改变

<p style="color: green;">another text<p>

#基本概念
Redux核心的概念就三个:Action、Reducer、Store

##Action
Action是一个对象,表示页面发生的事情,也就相当于事件

内部其实就是简单的具有一个type属性(常为常量)的JS对象,
描述了Action的类型以及负载信息
比如上面我们视图变化的例子

//Action
{type: 'TEXT_CHANGE',newText: 'another text',newColor: 'red'
}

###Action Creators
考虑到对它的复用,Action可以通过生成器(Action Creators)来创建
其实它就是返回Action对象的函数(我们自定义的函数)
参数可以适情况而定

//Action Creators
function change(text, color){return {type: 'TEXT_CHANGE',newText: text,newColor: color}
}

##Reducer
Reducer是一个函数,用于修改我们的单一状态树

还记得ES5的Array.prototype.reduce()函数么
我认为reduce中文释义中最接近这个函数功能的应该是“浓缩”
这个浓缩函数接收一个回调让我们将多个值浓缩为一个值
我们的reuder就可以作为(包含多个action对象的)数组的回调函数
###Pure Function
Reducer是一个纯函数(Pure Function)
(相同的输入会得到同样的输出)

纯函数(Pure Function)
概念源于函数式编程

  • 不能改写参数
  • 不能调用系统I/O的API
  • 不能调用Math.random()或Date.now()等不纯方法
    (相同的输入要得到同样的输出)

它获取应用的state和action作为参数,通过判断action的type属性返回一个新的state
伪代码表示就是:state + action => state
我们例子中的Reducer可以这样来写

//Reducer
const initState = {text: 'a text',color: 'red'
}
function reducer(state = initState, action){switch(action.type){case 'TEXT_CHANGE':return {text: action.newText,color: action.newColor}default:return state;}
}

由于纯函数这个条件的限制
所以我们不能改写参数中的state
所以比如说我们的state要表示列表
于是使用了数组来表示state
那我们就不能使用state.push()
如果state是对象也是同样的道理
可选方案如下:

  • 数组

    • 使用ES3的arr.concat(),得到新数组后返回
    • 使用ES6的展开操作符[…state, newItem],得到新数组后返回
  • 对象
    • 使用ES6的Object.assign({ }, state, changeObj),得到新对象后返回
    • 使用ES6的展开操作符{…state, newState},得到新对象后返回

只要记住一点就好了,在reducer中不能改变参数state

###combineReducers( )
上面我们说到了,整个应用的数据都保存在一个store中,就导致store非常庞大
可想而知,reducer也会十分巨大
不过,Redux库为我们提供了combineReducers( )函数
让我们可以自定义很多reducer函数,然后通过它来合并成一个大reducer
参数就是一个对象

import {combineReducers} from 'redux';
const reducer = combineReducers({a,b,c});

这样利用ES6的语法,会让state的属性名与reducer的函数名相同
如果不同名的话,可以这样写

const reducer = combineReducers({a: reducerA,b: reducerB,c: reducerC
});

下面模拟实现一个combineReducers( )函数
内部使用了数组reduce方法,忘了的同学戳这里:传送门

const combineReducers = reducersObj => {return (state = {}, action) => { //执行combineReducers后返回一个大Reducerreturn Object.keys(reducersObj).reduce((obj, key) => {obj[key] = reducersObj[key](state[key], action);return obj; },{});};
};/*
合成后的Reducer返回一个对象
{a: reducerA(state.a, action),b: reducerB(state.b, action),c: reducerC(state.c, action)
}
*/

##Store
Store是一个对象,它保存应用的状态并提供一些方法来存取状态,分发状态以及注册监听
全部state由一个Store来表示,Store维持着应用的state
Store 把Reducer和Action联系到一起
###API
API如下:

  • 获取状态:store.getState( )

    • 获取当前时间点的数据集合state
  • 更新状态:store.dispatch( action )
    • 修改状态的唯一方式就是dispatch一个action,store接收到action后自动调用reducer
  • 绑定监听:store.subscribe( listener )
    • 一旦状态改变,就执行绑定的函数;方法返回一个函数,调用可以解除监听
  • 改变reducer:replaceReducer( new-reducer )
    • 替换创建store时的reducer页面,比如页面跳转时使用(新的API,好像好少用)

API的使用也非常简单
比如我们试着这样做

store.subscribe(()=>console.log(store.getState()));
store.dispatch(change('another text','green'));
// 输出:{color:'green',text:'another text'}

首先我们(subscribe)绑定了一个listener
这个listener会让应用state更新后,就输出当前state(getState)
最后(dispatch)更新状态

###createStore( )

我们可以通过redux库中的createStore( )方法来创建Store
参数如下:

  • reducer(必选):

    • 必须为store指定一个reducer函数
  • preloadedState(可选):
    • 整个应用state初始值,通常由服务器发出
    • 如果选择使用这个参数,就会覆盖reducer的默认state初始值
  • enhancer (可选):
    • 一个函数,如果选择使用它,则会使用一个强化版的store creator
    • 源码return enhancer(createStore)(reducer, preloadedState)
import {createStore} from 'redux';
const store = createStore(reducer);
//或者干脆直接导入API
//let {getState,dispatch,subscribe} from createStore(reducer)

了解了它的功能,我们可以试着模拟一个简单的createStore( )函数

const createStore = (reducer, preloadedState, enhancer) => {if(enhancer){return enhancer(createStore)(reducer, preloadedState);} /*如果有enhancer,就使用强化版store creator*/let state = preloadedState, /*有preloadedState就设置为默认值,否则为undefined*/listeners = []; /*绑定的listener数组*/const getState = () => state;const dispatch = (action) => {state.reducer(state, action);listeners.forEach(listener => listener());} /*dispatch后,调用reducer更新state,一次执行listener*/const subscribe = (listener) => {listeners.push(listener);let index = listeners.length;return () => listeners.splice(index, 1);} /*subscribe添加listener,返回一个unsubscribe函数*/return {getStore,dispatch,subscribe}
}

这个只是帮助大家理解,所以没有写太细
更详细的可以参考源码

#工作流
如果了解了核心概念后一脸懵逼不要紧
继续往下看就懂了
使用React配合Redux构建项目的工作流如下:

Store由Redux的API-createStore(reducer)创建
页面触发事件由Action Creators返回action转发给Store
Store再将当前state和收到的action转给Reducer
Reducer处理后返回一个新state反馈给Store
Store管理的state改变了
就会引发React组件的重新渲染

再通俗一点儿
用户发出Action,Reducer更新state,View重绘


是不是发现Redux也就这点儿东西,豁然贯通 ︿( ̄︶ ̄)︿
(其实这只是最最基本的Redux,还有很多有用的API,理解容易,用好难)
当然如果仅仅是理解了,也只是纸上谈兵
由于本文只是浅谈Redux
就讲一个小小demo加深一下对Redux的理解

#小实例
这个实例就是一个简单的手动计数器
既然由Redux来管理页面的state
React组件中就不必使用state了
我们使用React的时候都是利用props、state改变而触发内部的render重绘
但现在我们利用React+Redux
就要自定义一个render函数再利用store.subscribe()绑定
这样store管理的state改变,就会触发我们自定义的render函数重绘页面

结构以及绑定的事件
事件处理函数也很简单,将action传递给store

const Counter = React.createClass({reduceHandler(){store.dispatch({type: 'REDUCE'});},addHandler(){store.dispatch({type: 'ADD'});},render(){return (<div><p>{this.props.value}</p><button onClick={this.reduceHandler}>-</button><button onClick={this.addHandler}>+</button></div>)}
});

构建reducer和store

const reducer = (state = 0, action) => {switch (action.type) {case 'ADD': return state + 1;case 'REDUCE': return state - 1;default: return state;}
};
const store = createStore(reducer);

创建渲染listener并绑定,每次store改变,触发重绘

const render = () => {ReactDom.render(<Counter value={store.getState()}/>,document.getElementById('root'));
};
store.subscribe(render);

不要忘了首次需要进行手动渲染
否则页面什么都没有

render();

主页传送门

应用数据流状态管理框架Redux简介、设计思想、核心概念及工作流相关推荐

  1. 面向对象设计思想-基本概念

    面向对象最需要的是 抽象的能力. 这个能力需要通过大量编写代码和思考系统结构来获得.类与类的关系,包与包的关系,项目与项目的关系. 架构师的抽象能力要更深一层,代码结构,数据结构,文件结构,项目结构, ...

  2. react全局状态管理_react状态管理redux

    Redux(上) 结合阮老师的技术博客,将自己吸收到的内容做了个整理: 曾经有人说过这样一句hua : 如果你不知道是否需要Redux,那就是不需要它. 从组建层面考虑,什么样子的需要redux:某个 ...

  3. React:Redux 设计思想

    Redux 背后的架构思想--认识 Flux 架构 Redux 的设计在很大程度上受益于 Flux 架构,可以认为 Redux 是 Flux 的一种实现形式(虽然它并不严格遵循 Flux 的设定),理 ...

  4. OLTP 系统和 OLAP 系统的核心设计思想

    关于 OLTP 系统和 OLAP 系统的核心设计思想 数据存储系统的关于查询的典型操作: -- 第一种需求: 根据 key(1) 找 value(name,age), 单点查询 select name ...

  5. Flutter主流状态管理框架provider、bloc、redux对比

    为什么需要状态管理 provider bloc redux 对比 总结 为什么需要状态管理 在了解和进行Flutter状态管理框架对比之前呢,我们先来问自己一个问题:为什么需要状态管理? 如果我们的应 ...

  6. [Redux/Mobx] Mobx的设计思想是什么

    [Redux/Mobx] Mobx的设计思想是什么 依赖收集.在Mobx中,定义了observable的属性,mobx会自动跟踪这个属性值的变化:在用了mobx与react的桥接库mobx-react ...

  7. Redux简介以及Redux应用程序中的状态更新方式

    by Syeda Aimen Batool 通过Syeda Aimen Batool Redux简介以及Redux应用程序中的状态更新方式 (An intro to Redux and how sta ...

  8. JSP→Javabean简介设计原则、JSP动作、Javabean三个动作、Javabean四个作用域范围、Model1简介弊端、JSP状态管理、include动作指令、forword动作、param

    Javabean简介设计原则 JSP动作 useBean动作 setProperty动作 getProperty动作 Javabean的四个作用域范围 Model1简介弊端 JSP状态管理 Cooki ...

  9. Redux从设计到源码

    本文主要讲述三方面内容: Redux 背后的设计思想 源码分析以及自定义中间件 开发中的最佳实践 在讲设计思想前,先简单讲下Redux是什么?我们为什么要用Redux? Redux是什么? Redux ...

最新文章

  1. VMware 克隆网卡无法启动
  2. Vmware 安装CentOS7时连不上网问题的解决
  3. 通过命令行使用 JAX-WS调用webservice
  4. 无效0_12位浙江高考生成绩被教育考试院判定无效,0分收场的原因很可惜
  5. java 获取sqlsession_获取Java的MyBatis框架项目中的SqlSession的方法
  6. Spring AOP(面向切面编程)
  7. GitHub入门详解
  8. vscode vim快捷键失效_VIM真的好用吗?到底要不要学习VIM?
  9. Python语言实现用requests和正则表达式方法爬取猫眼电影排行榜前100部电影
  10. EF中使用数据库的标量值函数
  11. Linux驱动开发经典书籍
  12. 读书笔记三——你的灯亮着吗
  13. oracle 存档终点修改,修改归档模式的存档终点 archive log list
  14. 王兴191条思考:一个顶级创始人的修炼与洞察
  15. 【第一篇】Qt学习与使用---将数字转换成中文大写的形式
  16. Oracle rac集群笔记
  17. NYOJ 1238 最少换乘
  18. mysql数据库之存储过程
  19. 【MySQL】 一张表最多支持多少个索引?
  20. 蓝牙、红外线与wifi 区别以及不同频段无线电磁波的穿墙和绕过障碍物能力(转)

热门文章

  1. hadoop3.3.0 编译环境搭建
  2. 用好Clear功能 让你感官不疲劳——rzxt.com
  3. oracle+union+连接,Oracle中union/union all/Intersect/Minus用法
  4. ASP.NET Core 正确获取查询字符串参数
  5. 代码生成器-mybatis-plus-generator
  6. 整理兼职网站资源分享
  7. MATLAB数学建模(四):机器学习
  8. 【408计算机考研】计算机网络——第1章 计算机网络体系结构
  9. 数据挖掘与机器学习:顺序与选择结构
  10. 黑群晖从无到有完善指南