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

详解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前端大讲堂”。


将VB5中的ToolBar变成平面的 将程序从任务列表中隐藏 -VB资料 将我自已的命令加入窗体的控制菜单栏-VB资料 VB利用 UnloadMode 来控制窗体的卸载? VB利用Form_QueryUnload询问使用者是否关闭窗口 利用VB的图片框实现屏幕的滚动 利用VB动态改变Windows显示模式的两种方法 利用VB控制窗口显示风格 利用VB实现浮动按钮 VB利用子类处理技术限制窗体的大小 奇形怪状的窗体 -VB资料 强制ComboBox弹出下拉框 -VB资料 强制和防止窗口重画 -VB资料 巧用“关于”对话框-VB资料 请问在VB中如何关闭别的应用程序窗口 确定屏幕分辨率-VB资料 让VB5支持动画光标 让窗口拒绝接受键盘和鼠标事件 -VB资料 让窗体处于最前面 -VB资料 让窗体居中 -VB资料 让所有的控件都动起来 -VB资料 让最大化和最小化按钮消失-VB资料 任意多边形的窗口-VB资料 如 何将「拒 绝 被 盖 住 的 窗 口 」还 原-VB资料 VB如何创建椭圆形的窗体? VB如何创建无Icon的窗体? VB如何防止Edit框中的Password不被非法获取 VB如何将鼠标显示成动画鼠标? VB如何利用程序拉下 ComboBox? VB如何让窗体右上角的X失效? VB如何使Form的背景图随Form大小改变 getchar ()的问题 ◆→访问csdn会让我“死机”! 纯技术咨询,请大家踊跃发言 一个串口通讯的问题。 一个庞大的计算计划,求解素数的间隔的 那句效率高点? 有没有sql server+asp开发的朋友,怎样把sql server数据库上传到服务器上,分绍有分! 如何注册使用控件,急!!! CView类中如何实现浮动的提示信息? 怎样给对话框创建一个视1? 报表计时用table,不用query,报表已设计好了,在显示预览时如果将一些不符合条件记录筛选择掉呢? 请问流是个什么概念? 我是初学,请指点 送分求购api用法及中文说明! asp页面可否设置关键字,使文章能被搜索引擎搜索到? ~~谁想做世界杯活力宝贝~~? 如何将StringGrid 中的数据保存为Html格式的表格 关于断点续传,请问大家 php中有没有 与 asp response.clear等同函数 单片机高手请看:中断中的中断,麻烦中的麻烦。 怎一个难字了得?挑战高手 如何改变LISTVIEW中各项的宽度 求《C++ Primer》中文版电子书下载;ejian@21cn.com 请问:在Asp中如何使用ODBC和SQL 注册表help... ListCtrl控件设为ICON属性,请问是否有subitem属性? Win2000系统去访问WIN98系统的服务器 PING的通 但打开的时候报参数出错! vb调用vc编的activex oem怎么老是连不上阿? 简单的入门问题:如何让菜单变灰? how add icon to one popup menu ? 怎么判断一个form对象变量为空呢? 发送和接收数据包问题? ActiveX问题,微软专家请进.<------------- 急 *.idc不能运行了,请问这是为什么? dbcontrols(泰山__抛砖引玉) 怎样才能够把ado记录集写到EXECL并进行打印呢? 如何判断字符串中,有几个指定的字符? xml数据文件倒入数据库,有什么好方法。 检查数据的有效性,最好是在哪一个事件中检查?怎样写,可否给点例子。 如何在DELPHI中调用FLASH图片。谢谢!有帮助的给分! 一个有关串列的question!HELP.. 怎样让对话框总在最前? 今天一个女孩脸红的对我说声谢谢 为什么dll调试跟踪不进去 菜鸟提问:请解释一下.vbs好吗?里面可以放那些东西,怎么引用.......等等 Delphi 2 BCB的问题:BCB中的"::", ".","->"有什么区别? *.idc不能运行了,请问这是为什么?谢谢 简单的数据库问题 CFileDialog()函数的参数问题????? php ed3.0 为什么不好使 大散分,快来答,答者有分! access 表中的datetime型字段在delphi中如何使用? 一束与水平地面成30度的入射光线射的地面,现在利用平面镜将光线沿水平方向反射出去,画出光路图并计算平面镜应与水平方向成多少度的夹角位置?(写出计算过程) 矩形花圃一面靠墙,另外三面围的栅栏的总长度是19米,如果花园的面积是24m²,求花园的长和宽.总觉得 但是我是原封不动的 打上去的 总长度是19米 当入射光线与平面镜成90度入射时,反射角改变多少度 四年级上册寒假学伴P9括展天地怎麽写 英语翻译有谁有英文的数学类论文及它的中文翻译?请发给我,急用 在初中第一次月考,成绩很不好作文 某玩具加工厂最近生产一批福娃向外出口,他们计划每天生产x箱,每箱装有福娃50个,供给5个商店销售.请你用式子表示平均每个商店日进货量是多少. 英语翻译2.2.Estonian tourism statisticsTourism has an important role in the Estonian economy.According to the research conducted in co-operation withthe Statistical Office of Estonia and the Estonian Instituteof Economics of Tallinn Technical Uni 《月考》 四年级数学同步下册冀教版10页拓展练习怎么做 当光线与平面镜成90度入射时,反射光线改变了( )度为什么 种7棵树,使其中的3棵树在一条直线上,共排成6行(每一行只有3棵树)你设计种树的位置 第一题第二题请在?处填上合适的小数,使每横行 竖行 斜行三个数相加的和都相等 由于甲型h1n1流感的影响,在一个月内猪肉价格两次大幅下降,有原来的,每斤16元下调到每斤9元,求每次下调的百分率是多少?设平均每次下调的百分率为x,则方程可列为? 7棵树排6行每行3棵该怎么排? 利用长12cm的旧围墙以及总长度为24的栅栏围成一个长方形花圃ABCD、 1、若长方形花圃ABCD的面积为64平方米1、若长方形花圃ABCD的面积为64平方米,求AB2、能不能围成面积为80平方米的长方形花圃 学校操场是一个长110米 宽65米的长方形 在它的四周栽上绿化带 绿化带周长至少多少米 一套桌椅的价格是105元,其中椅子的价格是课桌的3分之4.椅子每把是多少? 如图,在一面靠墙的空地上用长为24米的篱笆,围成中间隔有两道篱笆的长方形花圃,设宽AB为x米,面积为S平方米(1)求S与x的函数关系式及自变量的取值范围;  (2)若从设计角度出发墙的最小利 在长40米,宽30米的矩形操场四周设一个宽度相同的绿化地带,使所剩面积为原面积一半,则绿化带宽度为多少这是个填空题,我懒得算~ 一套480元的桌椅,椅子的价格是课桌的七分之三,课桌和椅子的价格各是多少元?用两种方法!一种解方程一种列式 6年纪寒假作业P9第2题自己写一首一字诗 一个牧童坐在牛上面吹着笛子,二棵柳树还有一个太阳,太阳旁边有一只燕子,还有几朵云.参照:一帆一桨一鱼舟,一位渔翁一钓钩.一俯一扬一场笑,一江 受甲型H1N1流感影响,猪肉价格下降了30%,设原来的猪肉价格为a元/kg,则现在的猪肉价格为( )元/kg.回收废纸用于造纸可以节约木材,根据专家估计,每回收一吨纸可节约3平方米木材,那么回 有7棵树苗,每3棵种在一条直线上,只能种6行.画图,两种方式. 初中七年级寒假作业P9和P43的答案急求急求 一棵树木的命运请马上回答,这是me今晚的作业啊,急~~~~~~~~!谁先回答就采纳谁的!快~!快~!一棵生长了三十年的树命运却不由自主区区的几十元钱就断送了她守侯一生的孤独 那个拿着卷尺的家伙 7棵树分别摆成3,4,5,6行,每行3棵,怎么摆?画图最好! su ban 怎样悄然无声的杀死一棵树 把7棵树栽成6行,每行3棵,应该怎样栽?请画图表示 中国少年儿童新闻出版社总社(寒假作业)的P9页的第二题从方格的任意一个字开始一次走遍所有的格子,路线不能重复,正好成为首位相连的五个连环成语.人 老 下 月定 胜 底 捞马 天 海 人 一束光线垂直入射到平面镜上 入射角多少度反射光与入射光的夹角为多少 度 ° 北师版小学数学四年级下册第二单元第二课三角形分类,谁有比较新颖的设计?我要讲课,急需这节课的教学设计,最好是比较新颖的, 数理报 八年级下数学人教版答案拜托各位大神 二年级的第一次作文泡温泉怎么写? 急求冀教版四年级数学下册教案? 银燕小学有一块长方形的花圃,在修建校园时,把花园的长增加了3米,于是面积增加了18平方米.现在这个花圃的面积是多少平方米? 1.要想使反射光线与入射光线之间夹角为90度,入射角应是多少度?2.一束光线垂直射到水平放置的平面镜...1.要想使反射光线与入射光线之间夹角为90度,入射角应是多少度?2.一束光线垂直射到水 光线AO为入射光线,要使光线的方向改变60度,平面镜该如何放置怎么花 银燕小学有一块长方形花圃,长8米.在修建校园时,于是面积增加了18平方米.现在这个花圃的面积是多少? 当光垂直射向平面镜时,入射角与反射角分别是多少度,当入射角是90度时,反射角是多少度也就是当入射角是90度时是不是就是相当于垂直射入 寒假学伴 鹭江出的 五年级上册 答案 梅山小学有一块长方形花圃,长8米.在修建校园时,花圃的长增加了3米,这样花圃的面积就增加了18平方米.原 一年级数学上册新人教版教案 第几和第几之间有几人 五年级上册寒假学伴鹭江出版社的第24页第五题能力广角怎么做? 怎样让一棵树死 初二上考册语文的作文有哪些! 在 两棵树 中本文通过描写什么和什么这两件事,向我们展示了什么道理 什么方法让大树死的最快 初二下学期考什么作文我不要题目,我初一时考写人的,初二上学期考说明文,不晓得初二下学期考什么? 福尔摩斯英文书重庆哪里卖就是上面 读了两棵树的境遇你悟出了什么道理 四年下册 认识方程式 教案有哪位好人知道,我下个礼拜非常需要...如题`` 初一第一次月考会考什么作文 农夫种的两棵树农夫在地里同时栽了两棵同样大小的果树苗.第一棵树拼命地从地下吸收养料,并将它储备起来,用它滋润每一根枝干.另一棵树也拼命地从地下吸收养料,打算早点开花结果.第二 让一棵树悄悄的去死是这样的 领居家一棵梧桐树都长到我们家里来了 到秋天我家满院子都是树叶 刮大风的时候都依到墙上 很危险 和他说过一年年的也没砍.在他们那边长着友不能去根部放 一束光线垂直射到水平放置的镜面上,若入射光线的方向不改变,而把平面镜转动10度则反射光线与入射光线的夹为多少 一条比直的大道两旁种树时,先定下两棵树的位置,然后其他树的位置就容易确定下来了,这说明? 怎么整死一棵树
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘