说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

详解React Flux架构工作方式

HTML文档下载 WORD文档下载 PDF文档下载
Flux是Facebook内部用来构建React应用的一套架构,该架构的最大特点是其倡导的单向数据流方案。本文将重点讨论Facebook Flux架构的工作方式,以及开发者应该如何在项目中使用它。

【编者按】作者Ken Wheeler在《 Getting To Know Flux, the React.js Architecture》一文中 讨论Facebook的Flux架构的工作方式,以及开发者应该如何在项目中使用它。为了让前端开发者更好地掌握本文内容,景庄对该文章进行了翻译,内容如下:

什么是Flux

Flux是Facebook内部用来构建React应用的一套架构。它本身并不是一个框架或库。它仅仅是一个用于完善React应用开发的一种新的应用程序架构,Flux架构最大的特点是其倡导的单向数据流方案。

Facebook还提供了Dispatcher的开源库。你可以将Dispatcher认为是一种全局pub/sub系统的事件处理器,用于向所注册的回调函数广播payloads。通常情况下,如果你使用Flux架构,你可以借助于Facebook提供的这个Dispatcher实现,并且可以借助NodeJS中的EventEmitter模块来配置事件系统, 从而帮助管理应用程序的状态。

组成结构

Flux主要包括下面四个独立的组件,下面简单地解释下:

  • Actions:帮助向Dispatcher传递数据的辅助方法;
  • Dispatcher:接收action,并且向注册的回调函数广播payloads;
  • Stores:应用程序状态的容器&并且含有注册到Dispatcher的回调函数;
  • Controller Views:React组件,从Store获取状态,并将其逐级向下传递给子组件。

让我们通过一幅图来简单看一下这四个部分的关系:


如何处理外部API

但是,很多场景下不仅仅包括应用程序内部的状态数据,还可能包括来自外部的数据,也就是如果我们需要消费RESTful服务,那么我们该如何将这些外部数据引入到Flux架构的单向数据流中呢?经过实践,我们发现,在Action中引入外部数据流是个不错的实践, 数据然后再被送到Store中。

在继续阅读之前,我强烈建议你阅读下Facebook提供的Flux官方示例程序的 源代码。在你简单阅读了这个例子的源代码后,我们再来详细的探讨Flux中这几个组件的作用:

Dispatcher

我们首先来看看Dispatcher的作用:你可以将Dispatcher看成是Flux架构中整个数据流程的管理者。或者你可以将它看成你整个应用的中央交换机。Dispatcher接收actions作为payloads,并且将payloads分发给所注册的回调函数。

那么它就是一个pub/sub吗?

不完全是。Dispatcher会将payload广播给所有向它注册的回调函数,并且包括了允许你以指定次序调用这些回掉函数的执行逻辑,例如在处理前等待数据更新。在Flux架构中只有一个Dispatcher,它是你整个应用的中央Hub。我们来创建一个简单的Dispatcher:

var Dispatcher = require('flux').Dispatcher;var AppDispatcher = new Dispatcher();AppDispatcher.handleViewAction = function(action) {  this.dispatch({    source: 'VIEW_ACTION',    action: action  });}module.exports = AppDispatcher;

在上面的代码中,我们创建了一个Dispatcher的实例,它包括了一个叫做handleViewAction的方法。创建这个方法的目的是为了让你能够轻松的区分, 是视图层触发的action,还是服务器/API触发的action。

在handleViewAction方法中调用了dispatch方法(该方法来自Dispatcher实现),它会将payloads广播给所有注册到它的回调函数 (注意,这里所说的回调是在store中注册给Dispatcher的)。然后,action会触发Store中相应事件,并最终体现在state的更新上。

可以用下面这张图来表示这个过程:


依赖

Facebook所提供的Dispatcher模块还有一个非常赞的功能,那就是它能够定义依赖,并在Store中向Dispatcher注册回调函数。 因此,如果你的应用的某个部分依赖于其他部分的数据更新,为了能够进行合适的渲染,Dispatcher中的waitFor方法将会非常有用。

为了能够利用这一特性,我们需要在Store中存储注册给Dispatcher的回调函数的返回值,这里,我们命名为dispatcherIndex:

ShoeStore.dispatcherIndex = AppDispatcher.register(function(payload) {});

然后在Store中处理每一个被分发的action时,我们可以使用Dispatcher的waitFor方法来确保我们的ShoeStore已经被更新了,示例代码如下:

case 'BUY_SHOES':  AppDispatcher.waitFor([    ShoeStore.dispatcherIndex  ], function() {    CheckoutStore.purchaseShoes(ShoeStore.getSelectedShoes());  });  break;

Stores

在Flux中,可以用Store来分领域的管理应用程序的状态,例如ShoeStore、AppStore等。从一个更高层角度来看,可以简单地认为应用的每一个部分可以对应于一个Store,Store可以用来管理数据,定义数据检索方法,此外,Store中还包括了注册给Dispatcher的回调函数。

下面我们来看一个简单的Store的例子:

var AppDispatcher = require('../dispatcher/AppDispatcher');var ShoeConstants = require('../constants/ShoeConstants');var EventEmitter = require('events').EventEmitter;var merge = require('react/lib/merge');// Internal object of shoesvar _shoes = {};// Method to load shoes from action datafunction loadShoes(data) {  _shoes = data.shoes;}// Merge our store with Node's Event Emittervar ShoeStore = merge(EventEmitter.prototype, {  // Returns all shoes  getShoes: function() {    return _shoes;  },  emitChange: function() {    this.emit('change');  },  addChangeListener: function(callback) {    this.on('change', callback);  },  removeChangeListener: function(callback) {    this.removeListener('change', callback);  }});// Register dispatcher callbackAppDispatcher.register(function(payload) {  var action = payload.action;  var text;  // Define what to do for certain actions  switch(action.actionType) {    case ShoeConstants.LOAD_SHOES:      // Call internal method based upon dispatched action      loadShoes(action.data);      break;    default:      return true;  }    // If action was acted upon, emit change event  ShoeStore.emitChange();  return true;});module.exports = ShoeStore;

以上代码可能做的最重要的一件事就是:我们使用NodeJS中的EventEmitter模块来扩展我们的Store。这使得我们的Store能够监听和广播事件。这也使得我们的视图/组件能够基于这些事件来更新。 这是因为我们的控制器视图(Controller View)会监听我们的Store, 并利用这一点通过发出事件方式通知控制器视图应用程序的状态发生了改变, 从而进行视图层的重新渲染。

在Store中还有一个值得注意的事,那就是我们使用Dispatcher的regiter方法来注册回调函数。 这意味着,我们创建的Store会监听来自AppDispatcher的广播,这里我们使用switch语句来 区分广播内容所对应的action。如果一个相关的action发生了,那么就触发一个change事件,并且监听 此事件的视图会根据新的状态(state)进行重新渲染。这个过程如下图所示。


在上面的代码中,ShoeStore中还包括一个公共方法getShoes(),它可以在控制器视图中被调用, 用于检索所有在_shoes对象中的数据,并且使用这个数据作为组件的状态数据。因为这是个非常简单 的例子,在实际应用开发中你可以使用更加复杂的数据处理逻辑。

Action创建器和Actions

行为创建器(Action Creators)是一组会在视图中被调用的方法(也可以在其他地方使用), 它们用于向Dispatcher发送actions。也就是说,我们使用Dispatcher分发的payloads,实际上就是actions。

在Facebook提供的例子中,action的类型常数被用于定义这些action所被应用的场景,并且是伴随着action一起被传递的。现在,在所注册的回调函数内部,这些actions可以根据他们的action类型来处理。

我们可以看一个简单的类型常数的定义:

var keyMirror = require('react/lib/keyMirror');module.exports = keyMirror({  LOAD_SHOES: null});

在上面的代码中,我们用了React的keyMirror库来生成我们的类型常数的Key/Value对。如果仅仅是看这个文件,我们大致可以猜到我们的应用会加载鞋子(shoes)。借助于类型常数,可以使得应用 中涉及到的事物变得更加地可组织,可以让开发者能通过这样的高层抽象来组织应用程序。

现在,让我们来大致的看一个对应的行为创建器(action creator)的定义:

var AppDispatcher = require('../dispatcher/AppDispatcher');var ShoeStoreConstants = require('../constants/ShoeStoreConstants');var ShoeStoreActions = {  loadShoes: function(data) {    AppDispatcher.handleAction({      actionType: ShoeStoreConstants.LOAD_SHOES,      data: data    })  }};module.exports = ShoeStoreActions;

在上面的代码中,我们在定义了ShoeStoreActions对象,并在其中定义了loadShoes()方法, 该方法内调用了来自AppDispatcher的handleViewAction方法,并且将相应的行为类型与 我们提供的数据相关联。现在,我们可以在我们的视图代码或API中引入这个action文件。我们可以调用ShoeStoreActions.loadShoes(ourData)方法来向Dispatcher发送payload,随后Dispatcher会将它广播出去。ShoeStore则会监听来自Dispatcher的广播,并且触发相应的事件来加载相应的鞋子。

控制器视图 Controller View

你可以简单的将控制器视图理解为React组件,这些组件会监听change事件,如果事件发生了, 就从Stores中获取应用程序新的状态数据。随后,可以将数据通过props逐级向子组件传递。这个过程大致如下图所示:


让我们来看看代码长啥样:

/** @jsx React.DOM */var React = require('react');var ShoesStore = require('../stores/ShoeStore');// Method to retrieve application state from storefunction getAppState() {  return {    shoes: ShoeStore.getShoes()  };}// Create our component classvar ShoeStoreApp = React.createClass({  // Use getAppState method to set initial state  getInitialState: function() {    return getAppState();  },    // Listen for changes  componentDidMount: function() {    ShoeStore.addChangeListener(this._onChange);  },  // Unbind change listener  componentWillUnmount: function() {    ShoesStore.removeChangeListener(this._onChange);  },  render: function() {    return (      <ShoeStore shoes={this.state.shoes} />    );  },    // Update view state when change event is received  _onChange: function() {    this.setState(getAppState());  }});module.exports = ShoeStoreApp;

在上面的代码中,我们使用addChangeListener来监听change事件,并且在接收到事件后更新应用程序的状态数据。

我们的应用程序状态数据存储在Store中,因此我们可以用Store中的公共方法来获取数据,然后将其设置为应用程序的状态。

完整的数据流程

前面我们依次解读了Flux架构中的每一个独立部分,我们有了对Flux架构中的每一个组件的一个大致的认识, 并且能大致的了解了Flux架构的工作流。在这之前,我们已经通过了几张图片描述了数据在组件之间的流向, 现在让我们将上面的子流程组合起来,以更好的理解Flux的数据流:


小结

在读完这篇文章后,如果你之前并不了解Facebook的Flux架构,那么希望你现在能够说理解了。 但另一方面,如果不在React的实际编程开发中使用这一架构,你是很难真正的体会到Flux架构的 特点的。

当你使用Flux后,你会发现没有Flux的React应用开发就相当于是没有jQuery的DOM遍历操作。也就是说, 没有Flux你可以照常进行React应用开发,但是你在代码组织和数据流程上会缺乏优雅的解决方案。

另一个方面,如果你想使用FLux架构,但你并不想使用React,那么你可以参考 Delorean,它是一个Flux框架,并且可以让你使用Ractive.js或者Flight。另一个值得参考的库是 Fluxxor,它实现了在紧耦合的Flux组件中包含一个中央Flux实例。

最后,再次说明,如果要真正的理解Flux,你就得在实际开发中体验它。

原文链接:Getting To Know Flux, the React.js Architecture

译者简介:景庄,前端工程师,关注Node.js、前端工程化。个人博客: http://wwsun.github.com。


欢迎加入CSDN前端交流群2:465281214,进行前端技术交流。  

也可参加CSDN前端大讲堂(微信公开课),享受高含金量在线公开课,与专家讲师在线切磋交流。

如何加入CSDN前端大讲堂?由于该群目前已超过人数限制,所以您首先不得不 扫描下面二维码,加CSDN编辑陈秋歌为好友,然后请她邀请您加入CSDN前端大讲堂微信群。加好友时,请务必注明“申请加入CSDN前端大讲堂”。


云领域:且看亚马逊、Rackspace、微软及谷歌四国争霸 十一位EMC高管读2013技术趋势 你准备好了吗? Yahoo:拥抱Hadoop和HTML5 苹果聘女黑客为系统安全员 曾让Vista推迟发布 移动游戏定价之谜:0.99美金行不通 9.99美金和19.99美金才是王道 2012末日年:《时代》评选年度十大App 使用HTML5 Web存储实现离线工作 紧跟亚马逊、谷歌步伐 微软下调Azure云服务价格 云计算一周热点回顾:Hadoop专家分享大数据技术工具与最佳实践 Python之父Guido Van Rossum离开谷歌 一月加盟Dropbox 图灵奖获得者Butler Lanpson:计算机在做些什么 华为高级技术经理Anoop Sam John:HBase的二级索引 秦令今:技术是竞争的核心,形成自己的特色! 兰旭:拒绝同质化,做自己的真材实料! Cloud Foundry直面PaaS对手 初具商用能力 IndieBros访谈:探寻《2+18+200万》背后的故事 移动周报:生存还是毁灭 HTML5未来路在何方? 开发者的新机遇:微软生态系统打开开源大门 阻碍创新:谷歌、Facebook请求驳回抽象专利概念 Apple专利战分析:战火不停 创新不止 史上最牛独立开发者:花20美元狂赚100万美元 Twitter VS. Instagram:照片分享大战爆发 第二届“龙芯杯”中国开源软件设计大赛圆满落幕 TUP Masters第六期实录:Jeffrey Richter讲解Win8应用开发与.NET4.5 [简讯]Bootstrap 2.2.2发布 官博宣布新特性 我们正处于API的黄金时代吗? 敏捷开发的26条至理名言 12月10日:1815年首位程序员Augusta Ada出生 TOP30+应用排行榜:十一月国内外综合榜单 抱大腿:Square礼品卡服务进驻Passbook 科技创新剑指开源产业 “龙芯杯”破题自主研发 WINCE的市场 控件ID有何用? 初学XML,请问ASP传回的中文字怎么是乱码? 请问圆锥体侧面积计算公式是什么?快阿,正在考试!!!!!!!!!!!!!! (♂逍遙剑♂) 过来聊聊!进来! 怎样用文件的形式对数据库进行备份? 能在标题栏添加控件吗?谁能告诉我? 谁知道在Activex控件中事件执行顺序的问题 3山好可怜,被女人抓住了把柄,知道了粗细长短,哎。。。 想在odbc里用程序的方法建立驱动,怎么实现? vc编的DLL怎样处理LIB在BCB中可以直接用哦?我用WINAPI那个约定 救命的!!! 深圳的“同志”好多啊!有一个哥们还向我推荐了一个同志网站,可惜我不是同志,如果有同志需要的话,可以去找那些深圳同志接头! 有懂EJB ,weblogic 大连的朋友吗? to fightwolf:give she some color to see see. 装delphi 6之前是不是要装ie5.5? 现在有一个问题:在大数据量传输的情况下,想采用异步传送方式,请问如何实现? 关于比例问题!! 有人说:做程序员很累,但我很快乐?广大程序员朋友们,有谁同意这个说法吗? 那该怎么办? MFC开发的ActiveX怎么都是OCX,可不可以是DLL,如果可以该怎么做。 如何获得当前活动控件的引用? 哪里有获得指定IP地址的机器网卡MAC地址的源程序? 新手上路:请问系统安全的市场前景如何? 'select * from a where b like ''[as]%'''这一句有没有错呢? 哪能找到 VBScript 5.0 的联机文档 为什么第七期的杂志我还没收到啊!!~~,原以为提前先订几期,会早点收到!唉!~~ php打开CSV文件的问题!答来分去! 各位有用过aspchart这个控件的吗?为什么我不能生成图片呢? 我有一个问题大家帮我UP一下 用HTML可以制作留言版吗?有使用mailto的吗? 大哥们,小弟我准备学JAVA了给推荐几本好书吧!小弟不胜感激啊! 因为JAVA,我差点把机子砸了 网络协议相关——向大侠请教 longyear,来一下啊。 我的一个在installshield for vc6.0下制作的安装程序用installshield professional 6.1编译时出错 在运行期间只有这个控键的句柄如何知道它是不是TForm? 如何动态生成组件? CTreeCtrl 的item项已被选中,再次点击怎么触发同样的事件?我明白了,你明白了没有? 为什么有冬瓜、南瓜、西瓜却没北瓜?同样的,为什么有北京、南京、东京却没西京?这个问题困扰了偶好久…… 请问GetPane得到的是什么啊? 号外号外,上海热线的吸铁石电子信箱有60M的空间,是真是假啊,我真怀疑自己看花眼了,哪位帮我确定一下,这1分就请您笑纳了 关于报表的问题--为什么细节带只打印一条记录? 初学者特来请教! 有谁能够告诉我关于RS-232口,RS-485口的有关信息???? 这个VB怎办??? 这个cookie该怎么实现? pe文件原理 请问这条SQL语句where有错? 在网络安全方面,DELPHI有没有发挥的空间? 请问在控制面板打开键盘对应的可执行文件是什么,我想应该是control .cpl,但是我找不到是那个cpl文件??? $1500元等于多少人民币? 力矩单位的换算大家好,请问1N.m等于多少N.mm?如果方便,麻烦请写一下换算过程. 1nm等于多少厘米 1500毫升=()立方分米填的是分数。 30 ft lbs 等于多少 psi? 1nm等于多少米 1500毫升=(?)升=(?)立方米 LBS和PSI 有区别吗在美标中的压力:3000LBS,是不是和3000PSI相同的. 20bar等于多少公斤力? 1500-2000毫升相当几杯的水啊? 扭力转换:5IN.LBS等于多少kgf.cm 怎样用英语读kg/m2如题请帮忙标出读的音标 重量和牛顿是怎样换算的要具体的换算公式啊 国外的扭力in-lbs换算成中国的标准n-m怎么换算,还有kgf-cm,8 IN.LBS是多少Kg啊, 宽914cm的布它的重量为28-30m/kg,换算成g/m2是多少?计算过程请写明白点啊!一楼的是不是少算了一个零? 牛顿和千克能换算吗?怎么换算呢? 125PSI等于多少KG?100LBS等于多少kg?请解释一下psi和LBS是指什么单位? 一公升的啤酒等于多少Kg啤酒 lb换算成牛顿还是千克阿? 一个水池的水深度为2米,池底有一个面积为10平方厘米的孔,用塞子塞住,塞子所受水的压强为多少 8g/立方厘米等于多少kg/立方米 汉字构造有六书之说,是指哪六书? 牛顿和千克的换算关于减速机选型,一般要计算扭矩,N*m,其中的“N”指的是牛顿力,或者说是千克力吧,现在问题是,有些地方说1Kg=1N(1米力矩上1牛可以拉动1Kg物体),有些地方说1Kg=9.8牛(自由 浮力甲是一个空水池,底部的排水口已用塞子塞住,放水至虚线处甲是一个空水池,底部的排水口已用塞子塞住,放水至虚线处,(1)现将一质量为60g,体积为750ml的空心密闭瓶放入水中,试计算该瓶 根据词语的不同意思写句子尖锐 (1)物体有锋芒,容易刺破其他物体的:---------------------------------------------------------------------------------------------------(2)(言论、斗争等)激烈------------------ 急公斤与牛顿换算问题0.05--0.6KG剩以平方厘米等于多少N剩以平方米? 一个水池的深度为2米,池底有一个面积为10平方厘米的孔,用塞子塞住,塞子所受睡的压强大小为多少帕塞子所受水的压力大小为多少牛 根据句子的意思写词语.1:觉得意外或惊喜.2:冬季最寒冷的时期.3:能适应各种环境 在任何环境中都能满足. 牛顿和重量之间是怎么换算的?1牛=?KG. 体积80立方厘米的物体,有36立方厘米露出水面,问物块所受浮力为多少?物块密度为多少?(g=10N/Kg) 根据词语的 不同意思写句子生气:生命力,活力: 公斤和牛顿换算关系 物体的体积1*10^-4m^3,浸没在某种未知液体时浮力是1.5N,求该物体密度(g取10N/kg) 根据词语的意思写句子 公斤和牛顿如何换算 kg与g的换算 根据下面句子的意思写词语.1.称心如意,感到非常满意的样子.2.争的向前,生怕落后.3.形容极度惊慌,行动失常的样子. 英磅/英寸怎么转换成牛顿/MM 比方说:25.5LB/IN转化成N/MM怎么转换 怎么将flv格式转换为3Q 《产品质量法》和国家标准《预包装食品标签通则》(GB7718)规定的食品标签必须标注的内容是什么? 牛顿怎样转换为吨 一千克等于几升? 规定国家标准格式的国标是哪个?国标号多少封皮丢了 想自己做一个也就是说国家标准的标准是什么? 液压扭矩扳手上显示单位是压力怎么转换力矩在使用液压扭矩扳手紧M30(10.9级)螺栓时,压力表上显示到350bar,能转换到力矩吗?有多大?M30(10.9级)预紧力极限值是多少?与压力表显示数值关系吗 一吨等于多少千克?一吨等于多少公斤? 胡萝卜素用英语怎么说 压力值怎么换算成力矩值液压力矩扳手上面的读数 (PIS mpa) 怎么换算成力矩值(N.m) 1公斤力厘米等于多少磅力英寸 我想请问下,GB2760-2011中,固体饮料类的食品添加剂没有β胡萝卜素,但是在其他的食品中属于食品添加剂, 力矩3530N.m 怎么转换成 液压泵的压力MPa扭紧螺栓的时候是用个液压扳手,用力矩扳手,是3530N.m 现在要换算成压力,因为我用的是液压扳手,要看压力的,怎么换算,或者缺什么条件不能换 1KPA等于多少BAR?我车胎上写着350KPA,是不是最大胎压是这么多?另外,车体上原写着的是2.2BAR的,那我是不是应该以车胎的为准?那么如果以车胎的为准,1KPA等于多少BAR? 请问GB2760?请问GB2760中的甘油和苯甲酸钠的化学名称是什么,它们的规定用量是多少,标准是什么? 1N等于多少g 一MPa等于多少Pa? 1500克等于多少两? 1N/25cm等于多少g/25mm还有,请问1N/25mm等于多少g/25mm? 1kpa等于多少mpa $1500等于多少人民币 螺栓预紧力矩为17T*M,其中的T*M如何换算成国际单位制! 电机中1nm力,请问可以拉动多少公斤的重物,大概下也好.
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn