资讯专栏INFORMATION COLUMN

Redux概念之一: Redux简介

cjie / 525人阅读

摘要:应用这说明并不是单指设计给用的,它是独立的一个函数库,可通用于各种应用。在数据流的最后,要触发最上层组件的,然后进行整体的重新渲染工作。单纯在的对象上是没有办法使用,要靠额外的函数库才能这样作,这是一定要使用类似像这种函数库的主要原因。

Redux的官网中用一句话来说明Redux是什么:

Redux是针对JavaScript应用的可预测状态容器

这句话虽然简短,其实是有几个涵义的:

可预测的(predictable): 因为Redux用了reducer与纯函数(pure function)的概念,每个新的state都会由旧的state建来一个全新的state,这样可以作所谓的时光旅行调试。因此,所有的状态修改都是"可预测的"。

状态容器(state container): state是集中在单一个对象树状结构下的单一storestore即是应用程序领域(app domain)的状态集合。

JavaScript应用: 这说明Redux并不是单指设计给React用的,它是独立的一个函数库,可通用于各种JavaScript应用。

有些人可能会认为Redux一开始就是Facebook所创建的项目,其实不然,它主要是由Dan Abramov所开始的一个项目,Dan Abramov进入Facebook的React核心小组工作是最近的事情。在此之前,他还有创建另外还有其他相关项目,像React Hot Loader、React DnD,可能比当时的Redux项目还更广为人知,在Facebook发表Flux架构不久之后,许多Flux架构的类似函数库/框架,不论是加强版、进化版、大改版等等非常的多。Redux一开始的对外演示的大型活动,是在2015年的React-Europe研讨会,视频Live React: Hot Reloading with Time Travel。在视频中就有一个简单的说明,Redux用了"Flux + Elm"的概念。

Redux = Flux + Elm

当然除了Flux与Elm之外,还有其他的主要像RxJS中的概念与设计方式,Redux融合了各家的技术于一身,除了更理想的使用在Flux要解决的问题上之外,更延伸了一些不同的设计方式。但是对初学者来说,它也不容易学习,网络上常常见到初学者报怨Redux实在有够难学,这也并不是完全是Redux的问题,基本上来说Flux的架构原本就不是很容易理解,Redux还简化了Flux的流程与开发方式。

所以我们要理解Redux是什么,我们开始可以从这Flux与Elm两大基础来理解,以下分别说明一些基本的概念。

Flux

不论是Flux或其他以Flux架构为基础延伸发展的函数库(Alt、Reflux、Redux...)都是为了要解决同一个问题,这个问题在React应用规模化时会非常明显,简单以一句话来说就是:

应用程序领域(app domain)的状态 - 简称为App state

应用程序都需要有App state(应用程序状态),不论是在一个需要用户登录的应用,要有全局的记录着用户登录的状态,或是在应用程序中不同操作介面(组件)或各种功能上的数据沟通,都需要用到它。如果你已经有一些程序语言或应用的开发经验,你应该知道这会像是MVC设计模式中的Model(模型)部份该作的事情。

React应用为什么会出现这个问题?原因主要是来自React组件的本身设计造成的。React被设计为一个相似于MVC架构中的View(视图)的函数库,当然实际上它可以作的事情比MVC中的View(视图)还要更多,但本质上的确React不是一个完整的应用程序开发框架,里面没有额外的架构可以作类似Model(模型)或Controller(控制器)的事情。对小型的组件或应用而言,应用的数据都包含在里面,也就是在View(视图)之中。

有学过React的一些基础的开发者应该会知道,在React中的组件是无法直接更动state(状态)的包含值,要透过setState方法来进行更动,这有很大的原因是为了Virtual DOM(虚拟DOM)的所设计,这是其中一点。另外在组件的树状阶层结构,父组件(拥有者)与子组件(被拥有者)的关系上,子组件是只能由父组件以props(属性)来传递属性值,子组件自己本身无法更改自己的props,这也是为什么一开始在学习React时,都会看到大部份的例子只有在最上层的组件有state,而且都是由它来负责进行当数据改变时的重新渲染工作,子组件通常只有负责呈现数据。

当然,有一个很技巧性的方式,是把父组件中的方法声明由props传递给子组件,然后在子组件触发事件时,调用这个父组件的方法,以此来达到子组件对父组件的沟通,间接来更动父组件中的state。不过这个作法并不直觉,需要事先规范好两边的方法。在简单的应用程序中,这沟通方式还可行,但如果是在有复杂的组件嵌套阶层结构时,例如层级很多或是不同树状结构中的子组件要互相沟通时,这个作法是派不上用场的。

在复杂的组件树状结构时,唯一能作的方式,就是要将整个应用程序的数据整合在一起,然后独立出来,也就是整个应用程序领域的数据部份。另外还需要对于数据的所有更动方式,也要独立出来。这两者组合在一起,就是称之为"应用程序领域的状态",为了区分组件中的状态(state),这个作为应用程序领域的持久性数据集合,会被称为store(存储)。

store(存储)并不是只有应用程序单纯的数据集合而已,它还包含了所有对数据的更动方法

store(存储)的角色并非只是组件中的state(状态)而已,它也不会只有单纯的记录数据,可能在现今的每种不同的Flux延伸的函数库,对于store的定义与设计都有所不同。在Flux的架构中的store中,它包含了对数据更动的函数/方法,Flux称这些函数/方法为"存储查询(Store Queries)",也把它的角色定位为类似传统MVC的Model(模型),但与传统的Model(模型)最大明显不同之处的是,store只能透过Action(动作)以"间接"的方式来自我刷新。

store的设计可以解决应用程序的状态存放与更动的问题,但它还不能完整的解决整个问题,只是一个开端。最困难的地方在于,要如何在触发动作时,进行store(存储)的更动查询,以及进行呈现数据的更动与最后作整个应用程序的渲染。这一连串的步骤,整合为一个数据流(Data Flow),Flux的名称来由其实就是拉丁文中的Flow,Flux用单向(unidirectional)数据流来设计整个数据流的运作,也就是说整个数据的流动方向都是一致的,从在网页上呈现的操作介面组件,被触发事件后,传送动作到发送器,再到store,最后进行整个应用的重新渲染,都是往单一个方向运行。

单向数据流是Flux架构的核心设计

下面是个简单的流程示意图,上面有标出主要的参与成员,来自Flux官网:

这个数据流的位于最中心的设计是一个AppDispatcher(应用发送器),你可以把它想成是个发送中心,不论来自组件何处的动作都需要经过它来发送。每个store会在AppDispatcher上注册它自己,提供一个callback(回调),当有动作(action)发生时,AppDispatcher(应用发送器)会用这个回调函数通知store

由于每个Action(动作)只是一个单纯的对象,包含actionType(动作类型)与数据(通常称为payload),我们会另外需要Action Creator(动作创建器),它们是一些辅助函数,除了创建动作外也会把动作传给Dispatcher(发送器),也就是调用Dispatcher(发送器)中的dispatch方法。

注: Payload用在计算机科学的意思,是指在数据传输时的"有效数据"部份,也就是不包含传输时的头部信息或metadata等等用于传输其他数据。它的英文原本是指是飞弹或火箭的搭载的真正有效的负载部份,例如炸药或核子弹头,另外的不属于payload的部份当然就是火箭传送时用的燃料或控制零件。

Dispatcher(发送器)的用途就是把接收到的actionType与数据(payload),广播给所有注册的callbacks。它这个设计并非是独创的,这在设计模式中类似于pub-sub(发布-订阅)系统,Dispatcher则是类似Eventbus的概念。Dispatcher类的设计很简单,其中有两个核心的方法,这两个是互为相关的函数:

dispatch 发送payload(相当于动作)给所有注册的callbacks。组件触发事件时用这个方式来发送动作。

register 注册在所有payload(相当于动作)发送时要调用的callbacks(回调)。这些callbacks(回调)就是上面说的会用来更动store的Store Queries(存储查询)。

在数据流的最后,store要触发最上层组件的setState,然后进行整体React的重新渲染工作。Flux提出的方式是一种自订事件的监听方式,把storeEventEmitter.prototype对象进行拓展,让store具有监听事件的能力,然后在最上层组件中的生命周期中,加入有更动时的监听事件。这是由于JavaScript中内建的Event、CustomEvent等介面,以及addListener、dispatch等方法,只能实作在具有事件介面的网页DOM元素上。单纯在JavaScript的对象上是没有办法使用,要靠额外的函数库才能这样作,这是一定要使用类似像EventEmitter这种函数库的主要原因。

不过,你可能会觉得为什么不干脆一点直接对store上面作更动就好了,一定要拐这么大一个弯,透过Action(动作)"间接"的方式来作自我刷新?

我想原因之一,是要标准化Action(动作)的规格,也就是所有在应用程序中的组件,都得要按照这些动作来触发事件,发送器中注册的callbacks(回调)也是要写成处理同一种规格的动作。Action(动作)主要由type(类型)与payload(有效数据)组成,Flux Standard Action(Flux标准动作)就是提出来要标准化Action(动作)的格式,有了统一格式的Action对象,在刷新数据时所有刷新方式会具统一性,这样Flux才有办法把整个数据流运作完成一个循环再接着下一个。就像网络的传输协定一样,数据的格式与运作的流程,都有标准的规范,不是随随便便就可以进行传输。当然还有一些其它的原因,例如要避免Event Chains(事件连锁)的发生。

整个的数据运作流程,大概是像下面这样:

事件触发 ->
由Action Creator调用Dispatcher.dispatch(action) ->
Dispatcher调用已注册的回调(callback) ->
调用对应的存储查询(Store Queries) ->
触发Store更动事件 ->
进行整个应用的重新渲染

下面是整个流程的示意图,来自Flux官网:

总结来说,Flux使用了单向数据流的设计架构,是为了要解决React的应用程序领域状态的问题。Flux的实作并不容易,有许多实作上的细节与开发步骤上都有分割不明确的问题,所以在此并不讨论Flux的实作部份。在Flux发表之后(约为2014年中),陆陆续续出现了许多函数库与框架,都是基于Flux的基本设计概念,都是为了要改善、简化或自动化其中的实作步骤为主,而Redux也是其中一套。在经过一段时间之后,目前较热门的与较多人使用的,就属Redux,它有很多的设计概念都来自于Flux,能多加理解Flux的基本设计概念,对于学习Redux是绝对有帮助的。

Elm

或许你有听过函数式程序开发(functional programming, FP)的开发风格,FP是什么?用下面的一句话来说明,摘译自这篇教程文档:

函数式程序开发就是只使用"纯粹函数"与"不可改变的值"来撰写软件应用的一种方式

FP是现今相当热门的一种程序开发风格,在很早之前就已经有一些纯函数式程序开发的语言例如Haskell与OCaml,Elm也是一个纯函数式程序开发的语言,它是一个很年轻的语言,Elm是专门用来开发网站应用程序的程序语言,最终编译为JavaScript在网页上运行,它与JavaScript语言有多差异很大的设计,例如:

Elm是强(静态)数据类型的,它的数据类型也满多样的

Elm是纯FP的语言

Elm-Architecture是包含在Elm的应用框架,它是单向数据流的架构

React与Flux中有许多设计,都有应用到FP的设计,与Elm中一部份设计相当类似。而Redux又使用更多Elm中的设计,尤其是Elm-Architecture而来的,例如:

不可改变性(Immutability): 所有的值在Elm中都是不可改变的,Redux中的纯函数(pure function)与Reducer的设计很类似,React的设计中也有这类的概念

时光旅行调试(Time Traveling Debugger): 在Elm有这个设计,Redux学了过来

Redux作者使用了FP(函数式程序开发)与Elm的架构,改进或简化原本的Flux架构

为何要学习Redux & Redux的优点

Redux是目前最热门的、最多人使用的Flux架构类的函数库,虽然Redux也可以用于其他的函数库,但基本上它是专门为了React应用所打造的。如果你真的要学会React,并用它来开发一个稍有规模的应用,学习Redux说是一条必经之路,当然也有其他的Flux架构类函数库可以选择,不同的函数库有可能使用的解决方式与样式相差会非常大。目前来说Redux的开发社群是最庞大也是最活跃的,而且不见得其他的函数库就会更容易学习与使用,毕竟用得人多,你会遇到的问题大概都有人遇过,也都能找得到解决方式,这是开源码生态圈的红利。

Redux会受欢迎不是没有原因的,以下分析几个Redux的优点:

使用了FP(函数式程序开发)与React可以配合得很好

Redux不同于Flux架构,它改采几乎是纯FP(函数式程序开发)的解决方式,目的是为了要简化Flux中数据流的处理实作,也的确可以与React中的组件渲染配合得很好,这证明了它是找到了一个较为理想的与React应用能密切合作的解决方式。FP(函数式程序开发)也是目前JavaScript界的热门主题,Redux也因此吸引到不少开发者的目光。

时光旅行调试/热重新加载

Redux一开始就附了时光旅行调试工具与热重新加载(hot reloading)的工具来提升开发体验,这对开发者有很大的吸引力,这也代表在Redux应用上的数据变动,可以更容易的测试与调试,这是其他Flux架构类函数库或框架中所没有的见到的。

更简化的代码,更多可能的延伸应用

Redux一开始的版本只有99行代码,这可能比一开始的Flux架构使用的API更要少,不过代码少不见得概念就简单,FP的撰写风格多半追求的是更简短的代码,这需要高超的技巧、深度的概念与不少的基础。Redux一开始就可以很容易的使用于服务器端渲染,而且也不限于使用于React应用上,这也吸引了更多的开发者使用意愿。

更多的文件,发展良好的生态圈

Redux作者一开始就撰写非常多的文件与教程,让许多开发者能更快捷地掌握Redux的应用技术,Redux作者也是技术讨论区的常客,常常可以看到他在讨论区上回覆相关的问题。Redux的项目也是相当活跃的,有非常多的参与者在讨论与解决问题,对于重大效能/臭虫问题也是很快捷地解决。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81351.html

相关文章

  • Redux技术架构简介

    摘要:是一个程序架构,源于提出的一种架构,然而,它不仅可以应用于,还可以应用于其他任何框架中。有以下职责维持应用的提供方法获取提供方法更新通过注册监听器通过返回的函数注销监听器。同时,的返回值实际上是一个函数可以解除监听。 Redux是一个程序架构,源于Flux(Facebook提出的一种架构),然而,它不仅可以应用于React,还可以应用于其他任何框架中。值得一提的是,Redux的源代码很...

    weizx 评论0 收藏0
  • 通过阅读源码来提高js知识

    摘要:在这篇文章中,分享了他如何克服恐惧并开始使用源代码来提高他的知识和技能。不久之后,你正在阅读的源代码将引导您进入规范。 通过阅读源码来提高js知识 原文传送门:《Improve Your JavaScript Knowledge By Reading Source Code》 showImg(https://segmentfault.com/img/remote/14600000197...

    浠ラ箍 评论0 收藏0
  • Redux技术架构简介(二)-- 异步实现

    摘要:异步实现设计需要增加三种通知异步请求发起的异步请求成功的异步请求失败的示例代码如下返回参数完全可以自定义。这种分别在请求开始前,请求成功后,请求失败后发送。表示数据的有效性,他的作用是在异步请求发送失败后,告诉当前的数据是过时的数据。 说明:对Redux不了解的同学可先看看这篇文章Redux技术架构简介(一) 前言 这里说的Redux异步实现,是专指Redux中的异步Action实现,...

    wuaiqiu 评论0 收藏0
  • 浅谈Redux之一):Middleware原理

    摘要:作为目前最火的模式实现之一,它有很多的点值得研究。这个函数既然要用于,也就是说它接收一个形式为的函数,对其一层层嵌套形式为。这个会在开始时发起一个,并在这个时发起另一个成功或失败的。为了方便起见,会返回这个让调用者可以等待。 Redux作为目前最火的Flux模式实现之一,它有很多的点值得研究。今天我们首先来看看它的Middleware。 熟悉Express或者koa的朋友对Middle...

    cocopeak 评论0 收藏0
  • 【全栈React】第18天: Flux 简介

    摘要:在方法中处理数据有三不同的角色派发器储存视图层我们的组件的主要思想是有一个单一源储存他们只能通过触发更新。这些操作负责调用派发器可以订阅更改并相应地更新自己的数据。与不同不使用派发器而是使用纯函数来定义数据变异函数。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3812原文:https://www.fullstackreact...

    mtunique 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<