Redux 是一种前端“架构模式”,是 Flux 架构的一种变种,用来提供可预测的状态管理。虽然经常和 React 一起被提及,但是 Redux 却不仅仅只能用于 React,还可以将其运用到其他前端库中,Vue Angular甚至是 jQuery。Redux 只是一种架构模式而已,并没有和其他库绑定在一起。而 React-redux 就是把 Redux 和 React.js 结合起来的一个库。就像 Vuex 一样,是一个与 Vue.js 结合的 Flux变种。

为什么要用 Redux

也许有人会问:为什么我们会需要 redux 呢?  嗯... 确实,我们必须要先了解我们为什么需要 redux? redux 的出现是为了解决什么问题?

那么,我们来考虑这么一种场景,在你构建的一棵组件树中,有A、B那么两个组件,它们需要共享同一个状态,你会怎么办呢?

我们可以通过状态提升的思路,将该状态提升到附近的公共父组件上面,然后通过 props 把状态传递给子组件,这样就可以在A、B组件之间共享数据了。确实可以,但是如果A、B的父组件在组件树向上好几个组件的位置呢?就需要将状态通过 props 一级一级往下传递,那么状态的传递路径就会非常长,而且中间组件根本就不需要访问这个状态。而且,如果后续有一个 C 组件也要访问该状态并且A、B、C的公共父组件还要往上呢?你就不得不修改之前代码了。很显然,这不是一种很好的解决方案,它会让我们的代码维护起来非常痛苦。

难道就没有其他方法可以解决这个问题吗?其实也有的,那就是 react 的 context,一个组件只要往自己的 context 里面放了某些状态,那么这个组件的所有子组件都可以直接访问这个状态而不需要通过中间组件的传递,看起来问题解决了嘛。

我们虽然解决了状态传递的问题却引入了新的问题,我们引入的 context 打破了组件和组件之间通过 props 传递数据的规范,极大地增强了组件之间的耦合性。而且 context 就像全局变量一样,里面的数据可以被子组件随意更改,可能会导致程序不可预测的运行。

这时候我们就该考虑使用 Redux 了,Redux 可以帮你创建应用的共享状态,并且不能随意的更改这些状态

Redux 的基本概念

我们已经了解了为什么要使用 Redux,那么我们先了解下 Redux 的三个基本概念。

Store

我们可以通过 createStore 来创建 store

import { createStore } from 'redux';
const store = createStore(reducers);

在 Redux 中,应用程序只能拥有一个 store,用来保存整个应用程序的 state,相当于一个应用程序的共享状态。

我们可以通过 store.getState() 来获取应用程序的当前状态。但是我们却不能随意的修改状态,我们只能通过 store.dispatch(action) 来修改状态。

修改完状态之后,我们希望可以做些 view 层的改变,这时可以通过 store.subscribe(() => {}) 来注册视图变化的回调函数。

Actions

Actions 是一个 JavaScript 普通对象,用来描述应用程序中发生的一些事情,也是把数据从应用传递给 store 的唯一途径。

我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作,type 一般会被定义成字符串常量。

const ADD_TODO = 'ADD_TODO'

{
type: ADD_TODO,
data: 'some data'
}

我们除了直接以 JavaScript 普通对象的形式来定义 action 之外,也可以通过函数形式来定义 action,这个函数被称作 Action 创建函数( actionCreator )。

const ADD_TODO = 'ADD_TODO';

function addTodo(data) {
return {
type: ADD_TODO,
data
}
}

这里 action 创建函数 addTodo 很简单,只是返回一个 action。 我们可以通过 store.dispatch 来通知需要修改状态

store.dispatch(addTodo('some data'));

Reducers

我们已经知道可以通过 action 来修改状态,但是 action 传递过来的只是简单的对象,并没有具体处理状态的逻辑,这就是 reducers 要做的事情了。

Reducer 必须是一个纯函数(一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,这个函数就叫做纯函数)。因为纯函数非常“靠谱”,执行一个纯函数不会产生不可预料的行为,也不会对外部产生影响。

function todoApp(state = { title: 'todoApp', todos: [] }, action) {
switch (action.type) {
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
data: action.data,
completed: false
}
]
})
default:
return state
}
}

todoApp 接收旧的 state 和 action,并返回新的state。注意这里我们是将 state 拷贝一份,再添加我们改动的值去覆盖原来的数据,重新组合成新的 state 返回,而不是直接修改 state。

这是因为如果你直接去改变 state 里对象的属性,那么就需要去比较新旧两个 state 的区别,而比较两个 Javascript 对象所有的属性是否相同就需要对它们进行深比较。但是在真实的应用中 js 的对象都很大,进行深比较的代价十分昂贵。而如果你返回的是一个全新的对象,就只需要比较新旧两个对象的存储地址是否相同就可以了。Redux 就是这么做的,如果你在 reducer 内部直接修改旧的 state 对象的属性值,那么新的 state 和旧的 state 将都指向同一个存储地址,Redux 会认为没有任何改变。

我们可以有多个 reducer,每个 reducer 只负责管理全局 state 中它负责的那部分,每个 reducer 的 state 参数可以都不同,分别对应它管理的那部分 state 数据。然后通过 combineReducers 组成根 reducer 用来创建一个store。

import { combineReducers } from 'redux';

const todosReducer = (state = [], action) => {
// do something
} const titleReducer = (state = '', action) => {
// do something
} const reducer = combineReducers({
todos: todosReducer,
title: titleReducer
}); // 等价于
function reducer(state = {}, action) {
return {
todos: todosReducer(state.todos, action),
title: titleReducer(state.title, action)
}
}

combineReducers 这个函数会调用你的定义的 reducer,每个 reducer 根据它们的 key(todos, title) 来筛选出 state 中的一部分数据处理并返回一份副本,根 reducer 会把这些副本组合起来形成一个新的大对象。最后根 reducer 将这个大对象传回给 store,store 再将它设为最终的状态。

Redux 工作流程

redux 一些基本概念我们都清楚了,我们来总结一下,Redux 为我们所做的事情:

1.  一个存放应用程序共享 state 的地方
2. 一个去分发 actions 通过纯函数修改应用程序共享 state 的机制
3. 一个可以订阅 state 更新的机制

严格的单向数据流是 Redux 架构的设计核心

我们只要清楚了 redux 中的数据流动的过程就明白 redux 整个工作流程了,我们从产生一个 action 的切入点来分析数据是怎样流动的。

1. 通过用户在视图层的交互产生了一个 action,这个 action 可能是通过 actionCreator 返回的。

2. store 接受这这个 action 之后,将当前的 state 和 action 一起传递给根 reducer。

3. 根 reducer 将 state 分配给子 reducer 进行处理,子 reducer 返回修改后的副本给根 reducer,根 reducer 整合子 reducer 返回的副本生成一个新的 state 副本返回给 store。

4. store 根据新的 state 触发视图层的渲染。

5. 用户看到交互后视图的变化,又高兴地发起了一个 action ...

更多精彩内容,欢迎关注微信公众号~

 

Redux 架构理解的更多相关文章

  1. React-redux: React.js 和 Redux 架构的结合

    通过Redux 架构理解我们了解到 Redux 架构的 store.action.reducers 这些基本概念和工作流程.我们也知道了 Redux 这种架构模式可以和其他的前端库组合使用,而 Rea ...

  2. 用原生JS从零到一实现Redux架构

    前言 最近利用业余时间阅读了胡子大哈写的<React小书>,从基本的原理讲解了React,Redux等等受益颇丰.眼过千遍不如手写一遍,跟着作者的思路以及参考代码可以实现基本的Demo,下 ...

  3. Redux架构

    深入Redux架构   阅读目录 关于redux API 中间件与异步操作 异步操作的基本思路 React-Redux的用法 回到顶部 关于redux 之前写了一篇通过一个demo了解Redux,但对 ...

  4. 在 React Native 中使用 Redux 架构

    前言 Redux 架构是 Flux 架构的一个变形,相对于 Flux,Redux 的复杂性相对较低,而且最为巧妙的是 React 应用可以看成由一个根组件连接着许多大大小小的组件的应用,Redux 也 ...

  5. TAF /tars必修课(一):整体架构理解

    来自零点智能社区 一.前言 TAF,一个后台逻辑层的高性能RPC框架,目前支持C++,Java, node 三种语言, 往后可能会考虑提供更多主流语言的支持如 go等,自定义协议JCE,同时也支持HT ...

  6. 沉淀再出发:Spring的架构理解

    沉淀再出发:Spring的架构理解 一.前言 在Spring之前使用的EJB框架太庞大和重量级了,开发成本很高,由此spring应运而生.关于Spring,学过java的人基本上都会慢慢接触到,并且在 ...

  7. redux的理解

    Redux 这里介绍下我对Redux的理解,不涉及如何使用Redux. Redux 官网介绍: A predictable state container for JavaScript apps.(一 ...

  8. ARM CORTEX-M3 内核架构理解归纳

    ARM CORTEX-M3 内核架构理解归纳 来源:网络 个人觉得对CM3架构归纳的非常不错,因此转载 基于<ARM-CORTEX M3 权威指南>做学习总结: 在我看来,Cotex-M3 ...

  9. RESTful 架构理解

    REST中的关键词: 1.资源 2.资源的表述 3.状态转移 资源: "资源",可以是一段文本.一张图片.一首歌曲.一种操作.你可以用一个URI(统一资源定位符)指向它,每种资源对 ...

随机推荐

  1. [LC] 107. Binary Tree Level Order Traversal II

    Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left ...

  2. 67)PHP,cookie的基本使用和基本原理

    (1)允许  服务器端脚本  ,   在浏览器端   存数数据的一种技术.   其实cookie是浏览器的一种技术. (2)特点:允许服务器向浏览器发送指令,用来管理存储在浏览器端的cookie数据. ...

  3. interrupt 停止线程

    该方法只是给线程设置了一个停止的标记 并不是真正的立即停止线程 interrupted() 测试当前线程是否已经中断 isInterrupted() 测试线程是否已经中断 停止线程的方法: .异常法 ...

  4. Hashtable和Hashmap的区别?

    1.实现的继承的父类不同 Hashtable继承Dictionary类    HashMap继承abstractMap类 两个类都实现了Map接口 2.线程安全性不同 Hashmap线程是不安全的 H ...

  5. unittest(9)- 使用ddt给测试用例传参

    # 1. http_request.py import requests class HttpRequest: def http_request(self, url, method, data=Non ...

  6. 修改npm安装的全局路径和配置环境变量

    我之前安装npm时全是默认安装,模块全部安装在C盘了,今天心血来潮,把路径改到了D盘,结果改完后模块都不能识别了,都提示XX模块不是内部命令,这其实是环境变量配置的问题,我都是按照网上的教程改的环境变 ...

  7. [SDOI2008] 洞穴勘测 (LCT模板)

    bzoj 2049 传送门 洛谷P2147 传送门 这个大佬的LCT详解超级棒的! Link-Cut Tree的基本思路是用splay的森林维护一条条树链. splay的森林,顾名思义,就是若干spl ...

  8. 【转】从技术和成本算笔账,自动驾驶L3过渡到L4有多难?

    转自:http://www.sohu.com/a/160479216_121787 从技术和成本算笔账,自动驾驶L3过渡到L4有多难? 2017-07-28 09:34 英伟达解决方案架构师程亚冰认为 ...

  9. failed to load main-class manifest attribute(运行jar包出错)

    原因描述:MANIFEST.MF文件中的Main-Class配置不正确或格式不正确 检查方式:以WinRarR的方式打开jar包,如图所示, 点击进入箭头所指的META-INF文件夹     将MAN ...

  10. Parentheses Balance (括号平衡)---栈

    题目链接:https://vjudge.net/contest/171027#problem/E Yes的输出条件: 1. 空字符串 2.形如()[]; 3.形如([])或者[()] 分析: 1.设置 ...