理解Javascript的状态容器Redux

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介:

Redux要解决什么问题?

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。
管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。
Redux的设计借鉴Flux、 Elm、Immutable,它的出现就是为了解决state里的数据问题。Redux和Flux的主要区别是Redux里面是单一的数据源设计,而Flux(或者Reflux)里面有多个数据源。

Redux是如何工作的?

我们知道,在React中,数据在组件中是单向流动的。数据从一个方向父组件流向子组件(通过props),由于这个特征,两个非父子关系的组件(或者称作兄弟组件)之间的通信并不是那么清楚。
React并不建议直接采用组件到组件的通信方式,尽管它有一些特性可以支持这么做(比如先将子组件的值传递给父组件,然后再由父组件在分发给指定的子组件)。这被很多人认为是糟糕的实践方式,因为这样的方式容易出错而且会让代码向“拉面”一样不容易理解。
当然React也没有直接建议如何去处理这种情形,以下是React的文档中关于这部分的描述:

对于非父子关系的组件,你可以自己建立一个全局的事件系统,Flux的模式也是一种可行的方式。

Redux的出现就让这个问题的解决变得更加方便了。Redux提供一种存储整个应用状态到一个地方的解决方案(可以理解为统一状态层),称为“store”,组件将状态的变化转发通知(dispatch)给store,而不是直接通知其它的组件。组件内部依赖的state的变化情况可以通过订阅store来实现。
使用Redux,所有的组件都从store里面获取它们依赖的state,同时也需要将state的变化告知store。组件不需要关注在这个store里面其它组件的state的变化情况,Redux让数据流变得更加简单。这种思想最初来自Flux,它是一种和React相同的单向数据流的设计模式。
1030776-20170112022416166-631450629.jpg

Redux的核心设计理念

Redux有三大原则

  • 单一数据源,整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中

  • State 是只读的,惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

  • 使用纯函数来执行修改,为了描述 action 如何改变 state tree ,你需要编写 reducers。

单一数据源的设计让React的组件之间的通信更加方便,同时也便于状态的统一管理。
根据Redux的文档,状态变化的唯一方式是触发一个action(一个可以描述发生了什么的对象),这意味着我们不能直接的去修改状态,取而代之的是我们可以通过转发action去告诉store我们有改变状态的意图。store对象提供了非常少的API,仅仅只有4个方法:

store.dispatch(action)store.subscribe(listener)store.getState()replaceReducer(nextReducer)

通过这几个API不难发现,store并没有直接提供setState()方法。
另外,由于它大量使用 pure function 和 plain object 等概念(reducer 和 action creator 是 pure function,state 和 action 是 plain object)这对于项目的稳定性会是非常好的保证。

理解Action、Reducer

一个action的例子:

var action = {
  type: 'ADD_USER',
  user: {name: 'Dan'}};// 假设store对象已经通过Redux.createStore()创建store.dispatch(action);

这段代码中,通过dispatch() 方法将action传递给了store。Action 本质上是 JavaScript 普通对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'

前面描述过,Redux不允许直接去改变state,必须通过转发action来告诉store有这个意图要去改变这个状态。reducer正是扮演处理转发过来的action的意图的函数并且可以改变状态的角色。一个reducer接受当前的state作为参数,通过返回新的state去改变原有的state:

var someReducer = function(state, action) {
  ...  return state;}

由于reducer是纯函数,需要注意:

  • 不允许在reducer函数内部进行网络调用或者数据库查询操作

  • 不能改变它的参数
    这样做的好处是:每次调用同样的一个函数,传入相同的值可以得到相同的结果。对系统的其它部分也不会产生副作用。

第一个Redux store

使用Redux.createStore()来创建一个store,并且将所有的reducers作为参数传递给它,此处以一个reducer为例子:

var userReducer = function(state=[], action) {
  if (action.type === 'ADD_USER') {
    var newState = state.concat([action.user]);
    return newState;
  }
  return state;}// 创建一个store,并且将reducer作为参数传递给它var store = Redux.createStore(userReducer);// 将action传递给store,告诉store我们有改变状态的意向store.dispatch({
  type: 'ADD_USER',
  user: {name: 'cpselvis'}});

上述代码运行后发生的事情:

  • Store被创建

  • reducer确定初始的state的值是空数组

  • action被转发给store

  • reducer将newUser添加到state中,并且将它返回,更新store

通过这段代码可以发现:reducer函数实际上被执行了2次,一次是store创建的时候,一次是action被转发之后。另外需要注意:Redux希望reducer函数总是返回一个新的状态。这时的结果:

store.getState();  // => [{name: 'cpselvis'}]

通过这个例子,可以总结出Redux的工作流程如图:
1030776-20170112135009463-1406392687.jpg

到这里,component -> action -> store -> reducer -> state 的单向数据流的问题就讲完了。概括的说就是:React组件里面获取了数据之后(比如ajax请求),然后创建一个action通知store我有这个想改变state的意图,然后reducers(一个action可能对应多个reducer,可以理解为action为订阅的主题,可能有多个订阅者)来处理这个意图并且返回新的state,接下来store会收集到所有的reducer的state,最后更新state。














本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1932209 ,如需转载请自行联系原作者







相关文章
|
2月前
|
JavaScript NoSQL Redis
深入浅出:使用 Docker 容器化部署 Node.js 应用
在当今快速发展的软件开发领域,Docker 作为一种开源的容器化技术,已经成为了提高应用部署效率、实现环境一致性和便于维护的关键工具。本文将通过一个简单的 Node.js 应用示例,引导读者从零开始学习如何使用 Docker 容器化技术来部署应用。我们不仅会介绍 Docker 的基本概念和操作,还会探讨如何构建高效的 Docker 镜像,并通过 Docker Compose 管理多容器应用。此外,文章还将涉及到一些最佳实践,帮助读者更好地理解和应用 Docker 在日常开发和部署中的强大功能。
64 0
|
2月前
|
运维 JavaScript 开发者
深入浅出:使用Docker容器化部署Node.js应用
在当今快速发展的软件开发领域,构建一套高效、可靠且易于扩展的开发环境成为了许多开发者和企业的首要任务。本文将探讨如何利用Docker这一强大的容器化技术,实现对Node.js应用的快速部署和管理。不同于传统的摘要方式,我们将通过一个实际操作的视角,逐步引导读者理解Docker的基本概念、容器与镜像的区别、以及如何构建自己的Node.js应用Docker镜像,最终实现应用的容器化部署。此外,文章还将简要介绍Docker Compose的使用,帮助读者管理包含多个服务的复杂应用。无论是刚接触Docker的新手,还是希望深化理解容器化技术的资深开发者,本文都将提供有价值的见解。
37 0
|
4月前
|
JavaScript 前端开发 API
js实现容器之间交换
js实现容器之间交换
59 0
|
JavaScript 容器
js基础笔记学习304添加容器
js基础笔记学习304添加容器
57 0
js基础笔记学习304添加容器
|
运维 JavaScript 前端开发
海纳百川无所不容,Win10环境下使用Docker容器式部署前后端分离项目Django+Vue.js
随着现代化产品研发的不断推进,我们会发现,几乎每个产品线都会包含功能各异的服务,而且服务与服务之间存在也会存在着错综复杂的依赖和被依赖关系,这就会带来一个世界性难题,项目部署的时候需要运维来手动配制服务之间通信的协议和地址,稍有不慎就会导致服务异常,同时如果服务器因为坏道或者其他原因导致更换物理机,重新部署新环境的成本也会非常之高。因此,我们就会寄希望于Docker这种的容器技术可以让我们构建产品所需要的所有的服务能够迅速快捷的重新部署,并且可以根据需求做横向扩展,且能够保证稳定的容灾性,在出现问题的时候可以利用守护进程自动重启或者启动容灾备份。
海纳百川无所不容,Win10环境下使用Docker容器式部署前后端分离项目Django+Vue.js
|
前端开发 JavaScript
JavaScript 监听移动端横竖屏状态的几种方式
JavaScript 监听移动端横竖屏状态的几种方式
629 0
JavaScript 监听移动端横竖屏状态的几种方式
|
JavaScript 前端开发 Cloud Native
阿里巴巴 Noslate 正式开源 - 面向云原生的 JavaScript 容器方案
继 2019 年开源 Midway 框架之后,阿里一直在 Node.js 的前沿进行深度研究,除了加入 TC39 参与标准化建设,向上游 Node.js 项目持续贡献,与龙蜥社区合作优化之外,也在 Serverless 领域有了不小的成果。
980 0
阿里巴巴 Noslate 正式开源 - 面向云原生的 JavaScript 容器方案
|
JavaScript 前端开发 Linux
Node.js 创始人:Javascript 容器是简化服务器抽象的“先驱”践行者
Node.js 创始人:Javascript 容器是简化服务器抽象的“先驱”践行者
140 0
|
JavaScript 前端开发 Linux
Node.js 创始人:Javascript 容器是简化服务器抽象的“先驱”践行者
Node.js 创始人:Javascript 容器是简化服务器抽象的“先驱”践行者
177 0
Node.js 创始人:Javascript 容器是简化服务器抽象的“先驱”践行者
|
人工智能 JavaScript 容器
【LeetCode】盛最多水的容器(JS实现)
【LeetCode】盛最多水的容器(JS实现)
152 0