引言:本文的目标是通过了解Flux 提出的模式,来明白Flux 的核心要点,以及弄清楚它到底是什么。并且,由于Flux不是传统意义上的软件包,因此我们将仔细研究通过Flux 来解决设计思路上的问题。

本文选自《Flux架构》。

  Flux 作为一种全新的方式,用于支持建立复杂的可扩展用户界面。当你在网上搜寻Flux的相关资料时,能了解到的大概也就是类似以上这些内容了。但我们该如何定义这样一种全新的方式呢?又是什么让其优于其他前端架构呢?

Flux 是一套模式

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

1 . 数据入口

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

             

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

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

2 . 状态管理

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

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

          

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

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

3 . 保持同步更新

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

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

        

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

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

         

4 . 信息架构

  我们常常忘记自己置身于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 可帮助我们摆脱这些问题。

1 . 数据流向

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

                      

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

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

              

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

  Flux 是一个单向数据流架构。这意味着,上图中的组件层是不可能出现的。现在的问题是,为什么说多向数据流不好?有时候,我们会觉得数据在各组件之间以任意方向传递

  是很方便的,这并不是个问题,因为传递数据不会破坏我们的架构。然而,当数据在系统中的移动是多方向的时,我们需要花更多的精力去为它们同步。简单来说,如果数据没有按照一致的方向进行流动,就有出错的可能。

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

               

2 . 可回溯性

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

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

3 . 通知的一致性

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

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

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

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

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

        

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

4 . 简捷的架构分层

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

                     

  上图主要描绘了数据在三个分层之间是如何流动的。(微信后台回复“Flux 组件”,我们将会对Flux 组件的类型进行引导性解释。)

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

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

5 . 低耦合渲染

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

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

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

  本文选自《Flux架构》,点此链接可在博文视点官网查看此书。

                     

想及时获得更多精彩文章,可在微信中搜索“博文视点”或者扫描下方二维码并关注。

                         

Flux架构的更多相关文章

  1. 探索从 MVC 到 MVVM + Flux 架构模式的转变

    本文首发于 my blog 在业务中一般 MVVM 框架一般都会配合上数据状态库(redux, mobx 等)一起使用,本文会通过一个小 demo 来讲述为什么会引人数据状态库. 从 MVC 到 MV ...

  2. 理解Flux架构

    本文摘自<Flux架构入门教程>和<谈一谈我对 React Flux 架构的理解>.也有自己的观点和总结.转载请注明出处. 一.Flux架构描述 1. Flux是什么 Flux ...

  3. react及flux架构范例Todomvc分析

    react及flux架构范例Todomvc分析 通过分析flux-todomvc源码,学习如何通过react构建web程序,了解编写react应用程序的一般步骤,同时掌握Flux的单向数据流动架构思想 ...

  4. Flux架构与Redux简介

    Flux架构区别于传统的MVC架构 在facebook实践中, 当用户接收到新消息时,右上角会弹出你有一条新消息, 右下角的对话框也会提示有新消息, 如果用户在对话框中查看了新消息,那么右上角的这个新 ...

  5. flux架构的详细介绍和使用!

    结构分为四个 视图 view动作 action派发器 dispatcher数据商店 store 流程: 用户操作视图 视图(view)发送动作(action)到派发器(dispatcher) 由派发器 ...

  6. 【原】flux学习笔记

    最近React(web/native)依旧如火如荼,相信大家都跃跃欲试,入职新公司,现在的团队也开始在React领域有所尝试. 2016年应该是React 逐渐走向成熟的一年.之前在原来公司搞不懂的问 ...

  7. 【原】整理的react相关的一些学习地址,包括 react-router、redux、webpack、flux

    因为平时经常去网上找react相关的一些地址,找来找去很麻烦,所以自己整理了一下,不过前面部分不是我整理的, 是出自于:http://www.cnblogs.com/aaronjs/p/4333925 ...

  8. 【go】脑补框架 Express beego tornado Flux reFlux React jsx jpg-ios出品

    http://goexpresstravel.com/ 今天 Express 的作者 TJ Holowaychuk 发了一篇文章,正式宣告和 Node.js 拜拜了,转向 Go 语言. Go vers ...

  9. Flux

    Ken Wheeler 构建React 应用的一套架构.  应用程序架构, 单向数据流方案. Dispatcher 的开源库.   一种全局pub/sub 系统的事件处理器, 用于 向所注册的加调函数 ...

随机推荐

  1. Java并发之——线程池

    一. 线程池介绍 1.1 简介 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡 ...

  2. Win10 IIS 安装.net 4.5

    更新Win10,原来的IIS站点访问不了,原因是因为IIS 没有.net 4.5,使用网上的aspnet_regiis.exe -i命令,一点都不靠谱,直接提示: C:\WINDOWS\system3 ...

  3. solr 利用cul或solr界面单值更新

    本文主要是指利用solr界面或cul的更新solr的值.如果需要代码中单值更新请参考黎明露珠的博客链接:http://www.cnblogs.com/limingluzhu/p/5535314.htm ...

  4. 14链表中倒数第k个结点

    题目描述 输入一个链表,输出该链表中倒数第k个结点.   思路: 快慢指针 快指针 先走k 步, 然后快慢指针一起走 当快指针走到null 时, 慢指针就是所求的倒数第k个节点 tips: 判断k是否 ...

  5. C++中的config设计

    配置文件读写类,它要有以下这些方法: 1. 支持读入一个指定配置文件的能力 2. 支持随时加入一个配置项的能力 3. 足够强大,能够写入各种数据结构的配置信息 C++ 里,我们要存储这样的数据就使用 ...

  6. Saltstack 命令行:批量发送命令,返回执行结果

    批量发送发送命令符,并返回结果. salt '*' cmd.run 'df -h' ---------------------------------------- Stest1: Filesyste ...

  7. Google揭露SHA-1碰撞,加速数据重删字节对比

    原创 架构师技术联盟  近期,Google和道荷兰阿姆斯特研究者宣布攻破了世界上第一例公开的SHA-1哈希碰撞实例,业界一片哗然.当两组不同的数据(文件.一段数据)计算出相同的Hash值时,即视为二者 ...

  8. MySQL java连接被拒绝:java.sql.SQLException: Access denied for user 'root'@'****' (using password: YES)

    //系统运行出现错误:java.sql.SQLException: Access denied for user 'root'@'***.**.**.**' (using password: YES) ...

  9. 用idea编译器写第一个Java程序——步骤

  10. oracle between、 all、 in 和 exists的区别,模糊查询、4个正则表达式

    --条件比较 /* =,!=,<>,<,>,<=,>=, any,some,all is null,is not null between x and y in(l ...