Flux架构(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-14 23:56:40

点击下载

作者:段金辰等

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Flux架构

Flux架构试读:

前言

我非常喜爱Backbone.js,它是一个神奇的库,只用少量代码就完成了大量功能。它并没有做出任何规定和限制,允许做同一件事情可以有无数种方式。这一点使得Backbone.js的开发者非常头疼!虽然,能随意采用任何方式去实现一项功能,是一件很棒的事,但这很容易导致许多潜在的问题。

当我首次接触 Flux时,我并不清楚该架构对 Backbone.js开发者有何帮助。最终,我发现了两点:第一,Flux 指定了实现方式,解决了 Backbone.js 的那个问题;第二,Flux与Backbone.js的创造灵感很接近,都是用少量部件来实现更多功能。

当我开始深入了解Flux时,发现了其在扩展方面所带来的魅力。当 Backbone.js 和其他相关技术在某些方面不正确时,即导致了它们的问题。实际上,这些漏洞极其难以解决,因为其整套体系都要因此而做出变通处理,来绕过这些问题。

我希望我写的这本书能够帮助各类 JavaScript 开发工程师从我正从事的来自 Facebook的这些技术中获得启示。

这本书包含什么

第1章初步介绍了Flux是什么及其存在的意义。

第2章介绍了Flux的核心原理和构建知识。

第3章逐步介绍了如何搭建一个框架,以便在后续章节中介绍具体的功能实现。

第4章介绍了当发生变化时,动作生成器是如何将数据填充到系统中去的。

第5章以举例的方式展现了异步动作生成器以及它们是如何在Flux架构中使用的。

第6章给出了许多详细的解释和示例,以阐述Flux存储器是如何工作的。

第7章给出了许多详细的解释和示例,以阐述Flux视图是如何工作的。

第8章讨论了Flux架构中的信息是如何进入系统以及最终是如何退出系统的。

第9章展示了不可变是软件架构的关键属性,如 Flux,数据流是单向的。

第10章讨论如何实现自己的分发器来替代Facebook的官方参考实现。

第11章展示了如何使用其他非React视图库技术配合Flux使用。

第12章介绍了两个非常流行的Flux库,Alt.js和Redux。

第13章讨论了Flux架构中的组件测试,以及架构的性能测试。

第14章讨论了Flux对于其他软件开发栈的影响和如何打包Flux的功能。

你需要提前准备什么

● 任何一款浏览器

● NodeJS 4.0或更高版本

● 代码编辑器

本书的读者对象

你是否有尝试使用React,但是又在使用 Flux的过程中绞尽脑汁的经历呢?或许,在处理可扩展应用时,你已厌倦了MV*模式中意大利面条似的代码?你内心是否在问什么是Flux?

Flux架构将引导你了解Flux模式和设计的方方面面,以及如何去设计和开发基于此的强大Web应用。

读本书之前,你并不需要事先知道Flux是什么以及它是如何工作的,而对它的关联技术ReactJS是零基础的读者,也同样适合阅读本书。不过,拥有较好的JavaScript知识还是很有必要的。

本书约定

在这本书中,你会发现一些文本样式,以用于区分不同类型的信息。这里列举一些样式,并解释其含义。

文本中的代码、数据库表名、文件夹名称、文件名、文件扩展名、路径、虚拟URL地址、用户输入和推特标签都将显示为代码体,如:当HOME_LOAD动作被分发时,我们会修改存储器中的状态。

代码块的样式如下。

警告或重要信息会呈现在此区域。

提示和窍门会以此样式呈现。

读者服务

轻松注册成为博文视点社区用户(www.broadview.com.cn),扫码直达本书页面。

● 下载资源:本书如提供示例代码及资源文件,均可在下载资源处下载。

● 提交勘误:您对书中内容的修改意见可在提交勘误处提交,若被采纳,将获赠博文视点社区积分(在您购买电子书时,积分可用来抵扣相应金额)。

● 交流互动:在页面下方读者评论处留下您的疑问或观点,与我们和其他读者一同学习交流。

页面入口:http://www.broadview.com.cn/316001 Flux是什么Flux作为一种全新的方式,用于支持建立复杂的可扩展用户界面。当你在网上搜寻Flux的相关资料时,能了解到的大概也就是类似以上这些内容了。但我们该如何定义这样一种全新的方式呢?又是什么让其优于其他前端架构呢?本章的目标是通过了解 Flux 提出的模式,来明白 Flux 的核心要点,以及弄清楚它到底是什么。并且,由于 Flux 不是传统意义上的软件包,因此我们将仔细研究通过 Flux 来解决设计思路上的问题。最后,我们将快速浏览Flux架构中的核心组件,然后安装Flux的NPM包,并写一个简单的Hello World程序来作为本章的结束。好的,让我们开始吧。Flux是一套模式

我们可能首先会在理解上遇到严酷的现实——Flux并非软件包;其实,它是一套方便我们去遵循的架构模式。这听起来似乎令人失望,不过别难过,有很好的理由不将其作为一个框架来实现。相比实际实现,在贯穿本书的内容中,我们将会看到Flux作为一套模式而存在的价值。现在,让我们开始了解Flux中的一些上层架构模式吧。数据入口

在传统前端架构设计中,我们很少考虑如何处理系统的数据入口。我们可能对此有个初步的方案,但是并不具体。例如,通过MVC(模型-视图-控制器)架构,让控制器来控制数据流。通常,这很有用。但另一方面,控制器实际控制的只是当数据已经存在后所发生的事情。那么控制器该如何在一开始就获取数据呢?如下图所示。

初看此图,似乎没什么问题。以箭头标识的数据流应该很容易跟踪。但数据从哪里来的呢?例如,通过用户事件,视图可以创建新的数据,并传递给控制器;根据各控制器之间的层次关系,一个控制器可以产生新数据并传递给另一个控制器。但关于控制器,它能自己创建数据给自己使用吗?

在类似这样的图中,这些问题看起来并不重要。但是,如果我们尝试将它扩展到拥有数百个类似组件后,数据入口在这个系统中的地位就非常重要了。因为Flux是一种用于复杂系统的可扩展架构,所以在这种架构模式下,数据入口是十分重要的。状态管理

状态是我们在前端开发中经常需要处理的。不幸的是,我们难以在无任何副作用的情况下整合所有的纯函数,这有两个原因:第一,我们的代码需要与 DOM 有正向或反向的交互,这也是用户在界面中所能感知到的;第二,我们不能把程序里所有的数据都存在DOM中(至少现在不能),这些数据会因为时间的推移和用户的操作而发生变化。

在Web应用中,并没有现存的状态管理的方法,但有多种方式来限制状态改变的数量,以及规定如何发生改变。例如,纯函数不能修改任何状态,它们只能创建新数据。以下是一个类似的示例。

正如你所看到的,此处的纯函数并没有副作用,因为,任何对它们的调用,都不会导致状态的变化。如果状态的改变是不可避免的,那么为什么还要采用这种做法?我们的方法是规定状态在哪里改变。例如,如果只允许部分组件类型可以修改程序里的数据状态,这样,我们就可以掌控哪些源可以引起状态变化。

Flux十分擅长控制状态在哪里发生改变。在本章的后面部分,我们会看到Flux存储器(Stores)如何管理这些状态的改变。Flux如何管理状态的重要性所在,是它在架构层上的处理。设计一套规则来决定哪些组件类型可以变更程序数据,这让我们感到很困扰,而相对于此,通过Flux则不需要花费什么精力来考虑在哪里更改状态。保持同步更新

数据入口点是同步更新的重要概念。也就是说,除了管理该状态变化的来源,我们还必须要管理这些相对于其他事务的变化顺序。如果数据入口点就是我们的数据,这时候我们应该同步地将状态的改变应用到系统中的所有数据。

让我们花点时间想想这为什么如此重要。在系统中数据被异步更新时,我们必须考虑竞争条件。竞争条件可能会产生问题,因为一个数据可能依赖于另一个,如果它们以错误的顺序更新,我们会遇到一连串的问题。下图说明了这个问题。

当事务是异步的时,我们无法控制何时发生状态改变。因此,我们所能做的就是等待异步更新发生,然后检查数据,并确保满足所有的数据依赖。没有自动化工具为我们处理这些依赖,我们只能写很多代码来检查状态。

Flux通过确保同步更新数据存储器解决了这个问题。这意味着,上图所示的情况不会发生。下图更好地展示了Flux是如何处理当今典型的JavaScript应用程序中的数据同步问题的。信息架构

我们常常忘记自己置身于IT行业,应该围绕信息发展技术。然而近期,风向大变,在新形势下,我们在探讨信息之前被迫考虑实现。通常,应用中所用的数据源暴露出的数据,并不是用户想要的。我们需要 JavaScript 将这些原始的数据变成用户可接受的数据。这就是我们的信息架构。

这是否意味着Flux被用于设计信息架构,而不是软件架构?并非如此。实际上,Flux组件被实现为真实软件的组件,用于执行实际计算。诀窍是,Flux模式使我们可以将信息架构作为首要的设计考量。不是非得通过各种组件及其实现问题来进行筛选,而是我们可以确保得到正确的信息给用户。

一旦我们的信息架构初具规模,更大的应用程序就会接踵而至,作为一种我们试图传达给用户信息的自然扩展。从数据中产生信息是困难的部分。我们不仅仅需要从大量的数据源中提取信息,并且这些信息也必须对用户产生价值。在任何项目中犯这种错误都将面临巨大风险。当处理正确时,我们就可以继续处理特定的应用程序组件,如按钮控件的状态等。

Flux架构保持数据在存储器中进行转换。存储器是一个信息工厂,原始的数据进入,新的信息产出。存储器控制数据如何进入系统、同步状态变化、定义状态如何变化。当我们跟随本书深入了解存储器后,将看到它们如何成为信息架构的支柱。Flux并不是一个框架

现在,我们已经对Flux的上层模式进行了一定的探索,让我们再来想一下:什么是Flux?其实,它只是一组可以应用到前端JavaScript程序中的架构模式。因为Flux将信息放在首位,从而使其具有良好的扩展性。信息是软件中很难扩展的一部分,而Flux推进了对信息架构的处理。

那么,为什么不将Flux模式实现为一个框架呢?如果这样的话,Flux需要提供一个标准实现给大家使用,像其他的大型可扩展开源项目一样,其代码也需要随着项目的发展而不断更新。

现在主要的问题是,Flux是在架构层上运行的,它用于解决阻碍已有程序扩展的信息问题,以满足用户需求。如果Facebook决定以一个框架的形式去发布Flux,那么就会遇到类似其他框架发展的困扰。例如,一些框架的组件不能在工作中以最适合的方式实现,如果不侵入框架,那就很难去实现一个更好的方案。

Facebook决定放弃实现完整的Flux是多么棒啊!他们确实提供了少量的Flux组件实现,但仅供参考。这些实现是有用的,但主要用来作为我们理解运作机制的起点,例如分发器如何像预期那样工作。我们可以随意以我们了解的相同的Flux架构模式来进行实现。

Flux并不是一个框架,这是否意味着我们需要去自行实现Flux?不,不需要。实际上,开发者正在实现Flux库,并将它们开源。一些Flux库很贴近Flux的架构模式;另一些Flux实现库有自己独到的理解,使用它们并不会有错,只要合适就行。Flux 模式旨在基于JavaScript开发解决通用概念问题,因此在深入探讨Flux实现之前要掌握其定义。Flux的设计思路问题解决方案

如果Flux只是架构模式的集合,而不是一个软件框架,那么它能解决什么样的问题呢?在本节中,我们将从架构角度来看Flux所能解决的设计思路问题,包括:单向数据流、可追溯性、一致性、组件分层和低耦合组件。这些问题都会在我们的软件中产生风险,尤其是大型可扩展应用。Flux可帮助我们摆脱这些问题。数据流向

我们正在建立一个信息架构,使得具有复杂功能的应用能够在此之上构建。数据流入系统,并最终到达终点,从而结束整个流程。在入口点和终止点之间所发生的就决定了Flux架构的数据流,如下图所示。

数据流的概念是一个很好的抽象,因为这可以很好地去可视化数据的流向,你可以很清楚地描述它如何进入系统,然后从一个点移动到另一个点,最终流动停止。但在它之前,会有些副作用发生,那就是上图的中间块所关心的问题,因为我们不能确切地知道这个数据流是如何到达终点的。

比方说,我们的架构并不对数据流有任何限制。任何组件都允许将数据传递给其他组件,无论组件的层次在哪里。请看下图。

正如你所见,我们的系统已经很明确地定义了数据的入口和出口。这太棒了,因为这意味着我们可以很自信地说出流经系统的数据流。但问题是,系统中各组件之间的数据流并没有在这张图中展现。图中数据流是没有方向可循的,更确切地说,是多方向的,这糟糕透了。

Flux是一个单向数据流架构。这意味着,上图中的组件层是不可能出现的。现在的问题是,为什么说多向数据流不好?有时候,我们会觉得数据在各组件之间以任意方向传递是很方便的,这并不是个问题,因为传递数据不会破坏我们的架构。然而,当数据在系统中的移动是多方向的时,我们需要花更多的精力去为它们同步。简单来说,如果数据没有按照一致的方向进行流动,就有出错的可能。

Flux强制数据流的方向,因而降低了因组件更新顺序不当而破坏系统的可能性。无论什么数据进入系统,都应当按照同样的顺序流入系统,如下图所示。可回溯性

我们知道,当数据流单向地从系统进入组件中的时候,很容易预测和跟踪所有可能会产生的影响。相反,当一个组件向其他任何一个组件发送数据的时候,却很难捕捉到数据是如何到达的。为什么会这样?通过调试器,我们现在很容易地可以在运行时遍历任何一级,无论有多复杂。但这个概念的问题在于,我们只是调试我们所需要的。

Flux架构中的数据流本质上是可预测的。这对于许多活动设计都是十分重要的,而不只是局限于调试。所以在用了Flux架构的应用中,程序开发者能很直观地知道即将发生什么。这种预期是很关键的,因为这样可避免让我们设计出死胡同一样的程序。当我们能够很容易地弄清楚因果时,就可以将大部分时间花在构建应用的功能上,因为这才是用户真正关心的。通知的一致性

在Flux应用中,我们从一个组件向另一个组件发送数据时,需要保持数据流向的一致性。在保持一致的时候,还需要考虑系统中的数据流向机制。

例如,分发和订阅就是一个在组件间通信十分流行的机制。这种方法所带来的好处就是,我们的组件可以相互通信,并且保持一定程度上的解耦。事实上,这在前端开发中是相当普遍的,因为组件的通信都是由用户的事件所驱动的。这些事件可以被认为是“即发即弃”的。任何其他组件想在某种方式上响应这些事件,都需要去订阅这个特定的事件。

分发和订阅的机制虽然有它的优点,但它在架构上也遇到了挑战,尤其是在大型复杂的应用中。例如,为了开发某项新功能,我们增加了几个新组件,但是以下这几点一直会困扰着我们:这些组件应该接受来自已有组件的消息更新吗?它们会得到这些更新响应的通知吗?它们应该先响应吗?这就是复杂性所带来的数据依赖问题。

分发和订阅带来的另一个挑战就是,我们有时需要先订阅一个通知,而后取消该订阅。这样,当我们试图在拥有众多组件的系统中,去尝试保持组件完整的生命周期,不仅十分困难,而且会增加丢失事件捕获的几率,从而破坏了一致性。

Flux方法,则是通过维持一个静态的内部组件信息树,来规避上面的问题,从而将消息分发到每个组件中去。换句话说,程序开发者并不需要去挑选组件所需要订阅的事件,而只需要区分出分发给它们的事件中哪些是相关的,剩下的则可以忽略。下面是关于Flux如何分发消息的示意图。

Flux分发器给每个组件发送事件,没有其他机制可以绕过这种方式。我们需要实现组件内的逻辑来判断此消息是否有用,以取代对消息结构的篡改而导致的难以扩展的问题。并在组件内部来声明对其他组件的依赖,这样对接下来的数据流向很有帮助。在接下来的章节中,我们会更详细地进行讲解。简捷的架构分层

分层是一种对组件进行组织的很好的架构方式。一方面是因为分层能够对应用的各种模块进行明确的分类,另一方面是因为分层可以对通信路径做出限制。后一点与Flux架构尤其相关,因为保持其数据流的单向性是十分重要的,而对分层做限制比对独立组件做限制要容易得多。下面所示的是Flux分层的示意图。

上图主要描绘了数据在三个分层之间是如何流动的,并没有展示Flux架构的完整数据流,也没有描述每个分层的细节。但别着急,下一节我们将会对Flux组件的类型进行引导性解释,而分层之间的通信则是本书的重点。

从上图中可以看到,数据从一个分层流向下一个分层,并且保持同一个方向。Flux仅有几层,应用的规模以组件数量来衡量,而分层的数量仍然是固定的。当给一个已经很庞大的应用增添新特性时,会使其复杂度达到上限。所以,除了限制分层数量和数据流方向, Flux架构也限制了哪些分层可以与其他分层进行通信。

例如,动作层(Action Layer)可以与视图层(View Layer)进行通信,并且该通信是单向的。我们可以编写Flux设计的分层,并且不允许跳过某个分层。确保一个分层只能与位于其下紧邻的分层进行通信,可以避免无序引入的代码问题。低耦合渲染

Flux设计的一个亮点在于架构不用关心UI元素如何被渲染,也就是说,视图层与架构的其他部分是低耦合的。这样设计是有原因的。

Flux首先是一个信息架构,其次才是一个软件架构。我们将首先学习其作为信息架构的思想,最后会学习其作为软件架构的思想。我们知道,视图技术的缺点是它会对架构的其他部分产生副作用,例如一个和DOM进行特殊交互的视图就会产生这样的影响,所以,一旦我们决定使用这项技术,它势必会对信息架构的组织方式产生影响。这并不一定是件坏事,但它将导致我们对最终呈现给用户的信息做出妥协。

实际上,我们真正应该思考的是信息本身,以及信息是如何变化的。哪些相关行为会导致这些变化?数据之间是如何依赖的?Flux不受当下浏览器技术的限制,这使得我们可以优先关注信息本身。因此,在信息架构中插入视图使其变为软件产品是很容易的。Flux组件

在本节中,我们将开始 Flux 概念之旅,这些概念是规范 Flux 架构的基本组成部分。尽管此处并不会详细介绍如何去实现这些组件,但这为具体的实现方式奠定了基础。本节将对这些组件进行概要性的介绍,并在本书之后的章节中进行实现。动作

动作就像系统内的动词一样。实际上,如果我们直接从一个句子中获得其中动作的名字,那将是很有帮助的,这些语句是对需要应用来实现的功能的典型描述,下面是一些例子。

● 抓取会话

● 导航至设置页

● 筛选用户列表

切换详情区域的显示与隐藏●

上面的这些都是一个Web应用所具备的基本功能,当我们将它们在Flux架构中实现时,动作就是起点。这些易读的动作语句通常需要依赖系统中的其他组件,但第一步总是从动作开始。

所以,Flux的动作到底是什么呢?简单来说,其实动作就是一个字符串,用来标识动作的目的。具体地讲,动作包含了一个名称和负载。先不要担心负载的细节,对动作而言,它只是发送给系统的黑盒数据。换句话说,动作就像一个个的邮件包。Flux系统的入口点并不关心这些包的内部是什么样的,而只关心它们应该去哪里。下面是一张Flux系统的动作示意图。

上图可能会让我们感觉动作是在 Flux 外部的,而实际上它们也是 Flux 系统的一个组成部分。这个观点是有价值的,因为它促使我们将动作作为给系统传递新数据的唯一途径。

Flux黄金法则:如果没有动作,一切都不会发生。分发器

分发器在Flux架构中承担的职责是将动作分发到存储器组件中(我们后面会讲到存储器)。分发器实际上像是一种代理,如果动作想将新的数据分发到存储器中,必须先通过代理,而代理能找到最好的方式去分发这些数据。试想一下,系统中的消息代理就像RabbitMQ消息队列,任何消息在实际分发之前都要经过中央枢纽。下图描述了Flux分发器接收动作并将它们分发到存储器中的流程。

在本章前面的“简洁的架构分层”小节中,并没有明确指出分发器在哪层,那是有意为之的。在Flux应用中,只有一个分发器。它可以被看作一个虚拟层,而不是明确的某一层。我们知道分发器就在那里,它并不是抽象。我们在架构级别所关心的是确定什么时候分发动作,并知道采用什么方式分发到系统中的存储器中。

前面说过,分发器对于Flux如何工作是至关重要的。它是存储器回调函数注册的地方,它决定了如何处理数据依赖。存储器告诉分发器它所依赖的其他存储器,并且分发器确保这些依赖被合适地处理。

Flux黄金法则:分发器是数据依赖的最终仲裁者。存储器

存储器是Flux应用中保存状态的地方。通常,前端通过API请求的数据会存在其中。然而,Flux存储器更进一步地模拟了整个应用的状态。如果这听起来让人感到迷惑,或者像一个糟糕的主意,不用担心,在接下来的章节中,我们将讲清楚这些内容,但现在我们只需要知道,存储器是能找到有这些重要状态的地方。其他的Flux组件没有状态,从架构的某一点来看,它们在代码层面上是有明确状态的,但是我们并不关心这些。

动作是新数据进入系统的传递机制。新数据并不意味着我们需要将其添加到存储器的某个集合里。从某种意义上来说,新数据进入系统后没有被作为动作分发,实际上它将导致存储器状态的改变。我们来看看以下这张存储器状态改变的示意图。

存储器如何改变状态的关键点是,并没有额外逻辑去决定状态该改变了。有且只有存储器可以决定状态的改变,并且完成状态的转换。以上是对存储器的高度概括。这意味着,当我们需要推理出某一信息时,只需要关注存储器。因为,它们掌控自己,可以自我布署。

Flux黄金法则:状态存在存储器中,并且只有存储器可以改变它们。视图

本部分介绍最后一个Flux组件:视图。从技术上而言,视图并不算是Flux的一部分,但它确实是应用中的一个关键部分。众所周知,在架构中,视图负责给用户呈现数据,它是数据流在信息架构中的最后一站。例如,在 MVC 架构中,视图接收数据模型,然后进行呈现。从这个角度而言,应用中的Flux视图与MVC视图并没有很大的区别,但在处理事件上,它们存在巨大的差异。让我们来看看下面这张图。

从上图中我们可以看出,Flux视图的职责与典型的MVC架构中的视图组件存在明显的区别。传入这两种视图的数据都是用于渲染组件的应用数据或者是事件(通常是用户输入),两者并没有什么不同,但从两种视图中输出的东西却存在区别。

不论事件处理函数与其他组件是如何通信的,传统视图都不会受到任何限制。例如,当用户单击按钮时,传统视图可以直接调用控制器中的相应行为,改变模型的状态,或者查询其他视图的状态。而Flux视图只能分发新的动作,这种方式可以保障进入系统单一入口的完整性和一致性,而不会与其他想要改变存储器数据状态的机制相冲突。也就是说, API响应更新状态的方式与用户单击按钮时发生的行为是完全一样的。

在数据流如何在 Flux 架构中输出(除了 DOM 更新)这一点上,视图需要受到限制。鉴于此,你可能会认为视图应该是一个真正的 Flux 组件。将动作作为控制 Flux 视图的唯一途径是有意义的,我们现在也有理由去强制这么做,因此,Flux才可以专注在创建信息架构上。

不过,要知道,Flux 现在仍旧处于它的初期。而随着越来越多的人开始使用它,Flux必将受到外部的影响。也许将来Flux在视图方面会有一些改变,然而,在那之前,视图还是存在于Flux之外的,但却受限于Flux的单向性。

Flux黄金法则:数据流出视图的唯一方式是分发一个动作。安装Flux软件包

在结束第1章之前,我们先写一段代码来感受一下,因为许多人都喜欢通过创建一个Hello World程序来开启学习之旅。同时,我们还将介绍如何构建这样的示例代码,这也将被应用到本书的其他地方。

在这里,我们将跳过Node和NPM的安装介绍,因为这方面的详细介绍在互联网上到处都是。我们假设你已安装好了 Node 并开始继续后续的操作。

我们需要安装的第一个NPM包是Webpack。这是一个非常适合于现代JavaScript程序的高级模块打包工具,包括对基于Flux所开发的应用的支持。我们将把Webpack安装于全局环境中,以便其命令可以在我们系统内被方便地调用到。

npm install webpack -g

有了 Webpack,我们可以编译本书中的任何代码示例。然而,我们的项目需要引用一些本地的NPM包,如下所示。

npm install flux babel-core babel-loader babel-preset-es2015 --save-dev

选项--save-dev 将把它们可能存在的开发依赖添加到我们的文件中。这仅仅只是项目的初始化,但并不需要每次都这样手动安装来运行本书中的代码示例。你所下载的示例中已经包含了一个 package.json文件,因此若要安装本地依赖项,只需简单地在该文件所在目录下,运行以下命令。

npm install

现在,我们可以用 webpack 命令来创建示例应用了。这将是本章中的唯一示例代码,因此,我们可以快速切换到终端窗口并运行webpack命令,去生成main-bundle.js文件。或者,也非常鼓励你去尝试使用webpack --watch命令,这将会对运行状态中的文件变动进行监听,以实现改动后的自动编译。

这个Hello World的示例代码,其实是作为本书中其余部分的铺垫。我们已经完成了对所有示例代码的初始化操作,包括安装 Webpack 和相关所需模块。现在我们开始看代码,先了解一下HTML部分。

上述代码是不是非常清爽?甚至连body标签中都没有任何内容。最重要的内容已经放在了main-bundle.js脚本文件中,而这其中的代码是由Webpack为我们生成好的。让我们看看其中的代码。

正如你所看到的,在这个Hello World的Flux应用中,并没多少代码。实际上,这里唯一的 Flux 所需组件就是一个分发器,它会分发一组动作,而后已注册的对应处理函数会被执行。

在这个示例中并没有出现存储器和视图,但别担心,这只是用于介绍如何安装Flux的NPM包,以及做一个简单的开始。小结

本章为大家介绍了Flux,还具体介绍了它究竟是什么。Flux是一套架构模式,可适用于 JavaScript 应用,以在数据流方面提供足够的帮助。若需解决浏览器兼容问题,或为了提升性能,可以寻求其他已有的工具,而非Flux,因为其并非一套已实现的框架。大部分Flux中定义的内容都是关于如何解决设计思路上的问题,例如单向数据流,正因为如此, Flux并没有具体的实现。

我们在本章中介绍了一些组件,它们也将继续用于本书的其他地方。为了确保这些组件能够正常运作,我们创建了一个Hello Word的Flux应用。

现在,我们已经掌握了 Flux 是什么,那么让我们来看看 Flux 为什么这么设计,在接下来的章节中,我们会详细介绍一下,驱动Flux应用的设计原理是什么。2 Flux的原则在上一章中,你已从非常上层的角度了解到 Flux 的一些核心原则,例如 Flux 的精髓——单向数据流。本章的目标是了解Flux的基本原则。我们将重新回顾一下在 MVC 中,关于扩展一个前端架构时会导致的问题,因为这将可以让我们从更深层次去了解单向数据流,以及它如何解决那些扩展问题。接下来,我们将通过Flux架构来解决高层次的组件问题,例如如何分离各部分和消除过深的层次结构。最后,我们将比较Flux架构中的各种状态,并介绍其相关概念。MV*所面临的挑战

MV*是JavaScript前端应用中普遍采用的架构模式。在这里,我们称之为MV*是因为现在有多个相关模式的变种,但每一个都以数据模型和视图作为核心。在本书后续的讨论中,我们将把它们当作相同的JavaScript架构来看待。

MV*设计模式非常糟糕,因此它并不能成为开发社区的推动力,而它之所以流行是因为它能够正常运作,以满足我们的需要。虽然 Flux 可以被认为是 MV*的一套替代方案,但仍旧没有必要去毁掉一个正在正常运作的程序。

世上可能并没有一套完美的架构,Flux也并不意味着很完美。本节的目标不在于贬低MV*和所有以此而建立的事物,但会去观察 MV*的弱点,并看看 Flux 在这些地方是如何做出更好提升的。关注点分离

MV*的一个非常有优势的地方在于,它能确保关注点完全分离。也就是说,每个组件都有它自己的职责,这种模式遍布整个架构。另外,关注点分离是单一责任原则,这将确保关注点的完全分离。

为何我们需要关心这些?简单说来,当我们将职责分离到各组件中时,系统中的各部分便可很轻松地解耦。这意味着我们可以修改任意一处,而这并不会影响到其他内容。这是任何软件系统中都想拥有的特性——不用关注架构。然而,这真的是我们从MV*中获得的,这真的是我们应该追求的吗?

举个例子,将一个功能拆分为五个清晰的职责,这或许并没有什么意义。对功能行为的解耦可能并不能实际解决任何事,因为,每当我们需要修改某处时,都需要访问这五个组件。所以,相对于帮助我们去精心制作一个智能架构,关注点的分离无异于阻碍了生产力。下方是一个示例功能图示,演示了集中职责的分解。

每当一名开发者需要拆分一项功能以了解它们是如何工作的时候,他们最终会因为在源代码文件中翻来翻去而花费更多时间。这项功能看起来支离破碎,并且在代码组织方面也并无明显优势。现在我们再来看看基于Flux架构所构建的组织模式,如下图所示。

Flux的功能分解方式使得一切更可预测。我们在这里省略了视图自身分解的情形的讨论,因为其并不属于 Flux 的一部分。对于 Flux 架构来说,我们所关心的是,当状态改变被触发时,正确的信息始终能传输给我们的视图。

你可能留意到 Flux 功能中的逻辑和状态,二者保持紧密的耦合。这与 MV*形成鲜明对比,MV*认为应用中的逻辑都应该是独立的实体,并可操作任何数据。而Flux正好相反,我们可以在状态附近发现负责修改其状态的逻辑。故意设计成这样,这暗示我们不需要花太多精力在关注点分离,否则会导致更多的不便而非帮助。

正如我们将在接下来的章节中所看到的,该数据与逻辑的紧密耦合是Flux存储器的特点。上面的图呈现了这一点,并包含了复杂的功能,这也使得增加更多逻辑和状态变得更为轻松,因为它们始终在功能的外层,而非夹杂在组件树中。级联更新

当我们有一个组件能正常工作是很棒的,这可以帮助我们处理很多事情,而且,更为重要的是,这一切还是自动化的。组件能为我们完成一切事情,而不需要一个接一个地手动触发这些方法。我们来看看右边这张图。

当我们将输入信息传入到一个大组件后,我们相信它能顺利地自动完成我们所预期的事情。这类组件最好的地方,莫过于我们只需要维护较少的代码。并且,通过组织其子组件间的通信,该组件能够知道如何更新自己。

这是级联效应的开始。我们将告知某一组件执行一些行为,这又会继续导致另一组件做出反应。我们给其提供一些输入信息,这会导致另一组件有所反应,并以此类推。很快,我们就很难搞清楚我们的代码接下来要干什么了。这是因为,我们所关心的事情,都在视图之下隐藏着。此即所谓有心栽花花不开。

上图其实并不至于太糟糕。当然,难以理解的程度取决于这个大组件里包含了多少个子组件,但总的来说,这是个可控的问题。我们再来看看下图,这是上图的衍生版本。

发生了什么?多了三个方框和四条线,这便造成了级联更新的复杂程度大爆炸。现在这个问题将不再是可控的了,因为我们难以简单处理这种复杂状况,并且许多基于这种自动更新的MV*应用都拥有不少于6个组件。

我们设想去封装一些能够自动更新的组件,这有些天真。其问题在于,这通常是不可行的,至少当我们计划还要对软件进行维护时是这样的。Flux避开了这个级联更新的问题,因为只有一个存储器可以改变它自己的状态,而且这通常是为了一个动作而进行的响应。模型更新的职责

在一个MV*的架构里,状态被存储于模型当中。若要初始化模型的状态,我们需要从后端API中获取数据。确切说来:我们创建一个新模型,然后告诉这个模型去获取一些数据。然而,MV*没有提及谁负责更新这些模型。一种说法是,控制器会来掌管对这个模型的控制。但实际上呢?

例如,在视图为了响应用户交互而触发的事件处理函数中,会发生些什么?如果我们只是允许控制器更改模型的状态,那么这个视图事件处理函数会直接联系控制器。下图是控制器在不同场景下修改模型状态的情景。

乍一看,这个控制器设置得很合理。它作为模型的包装来存储状态。这是一个很安全的假设:任何想要改变这其中任一模型的动作,都需要经过控制器。毕竟,控制这些动作是它的责任。无论是来自于API的数据,还是由用户触发的事件,如果它们要改变模型的状态,都需要联系控制器。

随着我们的控制器的增长,由控制器控制的数据模型修改需求,导致越来越多用于控制模型状态的方法出现。如果退后一步并看看所有这些积累的方法,我们会留意到一些没用的中间层。我们通过代理来修改这些状态,这么做能带来什么好处?

另一个导致在MV*中控制器尝试建立状态的一致更改是条死胡同的原因是,模型自己也可以这么做。例如,在一个模型中设置一个属性,会对其他模型的属性产生副作用。更糟糕的是,在系统的其他地方,可能会对该模型的状态进行监听,这也会导致级联更新问题。

Flux存储器解决级联更新的问题,是通过允许通过动作修改状态来达成的。该机制回答了这里讨论的关于MV*所面临的挑战。我们没必要担心视图或其他存储器直接对存储器中状态的修改。单向数据

Flux架构中的基石便是单向数据流了,例如,数据流向是从A点到B点,或者A点到B点到C点,又或者是A点到C点。像这样的方向很重要,其特征是单向数据流,而且规模较小、井然有序。所以,由于我们的架构采用了单向数据流,这便意味着我们的数据绝不可能从B点流向A点。这是Flux架构中的一个重要属性。

我们在之前一节中看到,MV*架构中的数据流具有难以辨别的方向。在本节中,我们会介绍一些体现单向数据流价值的属性。我们将开始看看数据流中的起始节点和终止节点,然后思考一下如何避免单向数据流的副作用。从开始到结束

如果数据流是单向的,那么它必须同时拥有开始节点和结束节点。换句话说,我们不支持无尽头的数据去肆意影响该数据流所流经的各组件。当数据流是单向的,并明确定义了开始和结束节点,那么我们是不会拥有闭环流程的。取而代之,我们在Flux中有一个数据流大循环,如下图所示。

这是Flux架构的一个最简单模式,但这确实有助于说明任何给定数据流的起始和终止点。我们将此称为更新轮回(Update Round)。从某种意义上来说,更新轮回是原子性的,它会运行至完结,其间无法停下,除非出现异常的抛出。

JavaScript是一门运行至完结的语言,即从一处代码块开始运行,直到结束。这非常棒,因为,一旦我们开始更新用户界面,在此期间不会有任何回调函数对其进行打断。唯一的例外是,我们自己的代码可能会去打断更新过程,例如,存储器逻辑分发了一个动作,想去修改存储器的状态。这对于 Flux 架构来说是个糟糕的现象,因为它会破坏单向数据流。为了预防这种情形,分发器可以检测出在一个更新轮回中是否存在分发操作。我们将在后面的章节中讨论更多的相关内容。

更新轮回肩负着更新整个应用状态的大任,而非只是针对订阅了某些类型动作的部分。这意味着随着应用的增长,我们的更新轮回也会增长。当一个更新轮回触及每一个存储器之后,它可能看上去像是数据单向流经全部的存储器,以下是示意图。

从单向数据流的角度来看,我们并不需要关心有多少存储器存在。重要的是需要记住,更新操作并不会被其他分发的动作所打断。无毒无害

正如我们在MV*架构中所看到的,关于自动修改状态的优势,也正是它的弱势。当我们在隐蔽的规则下面编程时,事实上还会产生一大堆副作用。这会导致无法扩展,主要的原因是,它其实并不会按照我们所想的流程,去把控所有隐藏的关联。Flux则尽可能地避免了副作用。

让我们一起来看看存储器。它们在我们的应用中是状态的仲裁者。当更新了状态,它有可能会导致另一处的代码也因此运行起来。这在Flux中确实会发生:当一个存储器改变状态,如果有视图在该存储器中注册了订阅,便可能会获得此次修改的通知。这是Flux中唯一可能产生的副作用,但这是不可避免的,因为我们有时确实需要在状态变更时更新DOM(文档对象模型)。而 Flux 的区别在于其在涉及数据依赖时避免产生副作用的方式。在用户界面中,对数据依赖关系的典型处理方式是,通知需要相关操作的依赖模型。想一想级联更新的情形,如下图所示。

在Flux中,当两个存储器间存在依赖时,我们只需在依赖存储器中声明这个依赖。这么做是去告诉分发器要确保被依赖的存储器先被更新,然后,依赖存储器就可以直接使用它所依赖的存储器中的数据。由此,所有的更新依旧在同一个更新轮回中执行。显式优于隐式

通过使用架构模式,大家通常倾向于,隐藏背后那随着时间流逝而变得愈加复杂的抽象概念,来将事情变得简单。最终,系统中越来越多的数据被自动修改,开发者的便利性被隐藏的复杂性所替代。

这是一个显式的扩展性问题。而Flux通过明确动作和数据转化来处理它,而非隐藏抽象概念,这么做更有优势。在本节中,我们将探索和权衡显式所带来的好处。暗藏隐患的更新

我们已经看到,在本章中,通过隐藏抽象概念来处理隐藏状态变更是多么困难。这虽然能减少一些代码,但当我们回过头再来看这些代码时,却也让我们难以理解整个工作流。有了Flux,状态便可以保存在存储器中,并且存储器会对修改其自身状态负责。当我们了解某一存储器如何更新状态时,所有的状态变换代码就在那里,这就像在仙境一般。让我们来看看下面的存储器示例。

在这里,我们有一个存储器,里面包含一个简单的state对象。在构造函数中,存储器在分发器中注册了一个回调函数。所有的状态变化都明确地放在了一个函数里,这就是将数据呈现到用户界面上的地方。我们再也不需要紧紧地跟着数据在各组件中穿梭,因为在Flux中不会出现这种情况。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载