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

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


写小执行程序-Delphi资料 隐藏和显示Windows的任务条-Delphi资料 隐藏桌面上的图标-Delphi资料 用DELPHI编制Windows95下的钩子函数 用Delphi的THML控件实现触摸屏的使用 用Delphi实现禁止用户关闭Window9X 用Delphi制作能够干净地删除自己的程序 用DELPHI 做一个简单的屏幕保护程序 用Enter键代替Tab键-Delphi资料 用修改文件时间的方法来加密文件-Delphi资料 用最原始的方法编制程序-Delphi资料 运用Delphi编写Windows NT中服务程序 在DELPHI编程中确定系统运行模式 在Delphi程序中调用控制面板设置功能 在DELPHI下读取与设置系统时钟 在Delphi中避免2000年问题的捷径 在Delphi中调用API函数 在Delphi中获取和修改文件的时间 在Delphi中控制扫描仪 在Delphi中实现对WIN9X应用程序使用权限的设置 在Delphi中实现对目录拷贝、删除和搬移的操作 在Delphi中使用自定义光标 在Delphi中用拼音首字符序列来实现检索功能。 在DELPHI中用线程排序 在Delphi中制作背景音乐 在Dephi中使用TStream读写数据的技巧-Delphi资料 在应用程序中跟踪MOUSE的坐标-Delphi资料 怎样建立回调函数-Delphi资料 怎样显示自定义鼠标光标(Cursor)-Delphi资料 找出消失的 Delphi 窗口 执行外部程序-Delphi资料 神啊,救救我吧! 神啊,救救我吧! 菜鸟提问,如何知道MDB数据库中是否存在一个指定表,谢谢!急…… 关于用DBGRID向数据库添加主细表的问题?高分奖赏! 关于窗体菜单的问题? 很急!!!!!!!! 欢迎大家推荐好的学习JSP的网站! 数据库 vfp->sql server? 问一个最好的查漏方法 ARM Develop Suite eval version的破解方法或者破解文件 一个word文件,怎么让用户一打开它时 出现保存对话框? ie6.0的快捷键问题 这里人气旺,借贵宝地一用。熟悉网站搜索引擎的朋友请进! 交换机和路由器有什么区别?? csdn怎么回事啊!!昨天登陆后竟然显示别人的用户名,今天竟然连我的注册信息也改了.TMD 关于SQL SERVERS2000 建数据库的问题--请进 请问哪有讲算法的好书啊! 关于单片机??? 向各位大侠请教:怎样在JAVE中调用文件 SYBASE 数据库设备删除后 Vitual Device Number 不能释放? 高分诱惑(痛苦莫过于分太多) RichEdit中的COM问题!!! 各位师兄师姐,帮帮我,好吗? 一个图形文件,如一个JPG图形文件,怎么才能通过流读进来,然后转化成一个字节数组。 关于Activeform中INF文件的问题。(解决问题者有分) 我不算是ASP的学习者,但想请教个问题,一小段ASP代码 请推荐国外(or 国内)优秀的编程网站,for delphi 或vc等等 请指教:关于管理软件权限制作方面的问题 求救:关于从XML文档中抽取文本的方法!!! 怎样读取系统时间,日期?急! 请推荐一两本shell编程方面的好书。UNIX,LINUX不论。 ~◎大虾们,在linux下怎么得到一个进程的cpu mem的使用情况,用从c/c++程序! 我想学JAVA,请问如何开始阿。 键盘有一power键,按下就会关机,怎样编程使按下该键时不执行关机动作? 请问如何控制SourceSafe的物理权限 ? 各位兄弟,有谁知道在网上哪有SQL SEVER2000的教材下载,谢谢! 图片上传到服务器的一个固定文件夹里的程序怎么写??? 两个小问题! 各位兄弟,有谁知道在网上哪有SQL SEVER2000的教材下载,谢谢! ActiveX,在哪里找到它? 请教高手,关于c run-time library 的详解. 链接库为何链接不上??已经忙了2天了,求助 求助:关于数据与显示的问题 怎样更改默认的最大连结数 求助高手关于内存dc的问题 大侠救命~~~~~ 关于一个获取本机可用端口的问题 QuickReport 与 ADODataset的问题!其中ADODataset含有动态参数! VC++6.0的一个项目里只能有一个C源程序文件吗? VB 报表问题 急!!!高分求解!!!!! 新手上路:我已经按照MSDN创建了一个TempConvert的WebService,但是不知道下一步该怎么做才能使用它,请各位热心人指点!谢谢 一份满意的答卷600字的作文快各位帮个忙 孔乙己,变色龙,范进中举都是小说吗 上了初中后总觉得自己的心很烦,怎么熬过初中三年啊 桑娜为什么忐忑不安,她明明知道自己的五个孩子已经够丈夫受的了,为什么还要把西蒙的孩子抱回家?回答 变色龙与范进中举在讽刺方面有什么不同 我要一份检验论文,能写的找我! 初中水平的一份满意的答卷的作文600字 以 我最满意的答卷 为话题的作文 以考验为题写一篇作文怎么写啊? 桑娜脸色苍白神情激她忐忑不安地这段文字主要写桑娜什么用成语这段文字主要写桑娜什么?用文中一个成语准确说明希望能很快有答案哥哥姐姐帮个忙 描写人物心情的四字词语 有关中华儿女报效祖国的读后感从自己读过的描写中华儿女报效祖国、为国争光的文章或书籍中,选择最打动自己的一篇(一本),写一篇读后感.要表达真情实感. 李世民和武则天是什么关系 《十六年前的回忆》课后练习题的选做题 在精明前后祭扫烈士墓的感受 读中华儿女报效祖国的读后感急400字 武则天和李世民有关系吗? 十六年前的回忆可怕的一天果然来了.4月6日的早晨,妹妹换上了新夹衣,母亲带她到儿童娱乐场去散步了.父亲在里间屋里写字,我坐在外间的长木椅上看报.短短的一段新闻还没看完,就就听见啪, 中华儿女报效祖国读后感500字,10月20号要 ①因为有了期盼 ②一份满意的答卷 ③向目的地进发 每篇作文怎么写,给点提示, 十六年前的回忆啊!我要课后题第四题!词语课文里找! 对初中三年的打算 作文 不少于1000字 今天要 一份满意的答卷 作文600-800记叙文 请问有什么题材可写?目前因为还没头绪,明天早上要交呢老师要求挺严格的.还分析了"份"和"张"是不同的,份 字 更重些说我审题不仔细,全班重写(⊙o⊙)! 《孔乙己》和《变色龙》的比较赏析 我想给老师写封信,给老师道歉吧,快毕业了,初中三年给老师惹了不少麻烦.反正就是关于早恋,打架,上课不听讲,谢谢急啊! 为什么我照镜子感觉很好看,然后自拍又丑了些 但是我拿着手机拍镜子里的自己又挺好看的.到底哪个是为什么我照镜子感觉很好看,然后自拍又丑了些 但是我拿着手机拍镜子里的自己又挺好 初中三年的语法(全一点) 月亮上的足迹的体裁以及主要内容和中心思想不用复制一大堆,简单明确即可 武则天和唐太宗李世民 唐玄宗是什么关系 另外武则天是谁的皇后 跪求变色龙中人的笑与孔乙己中人的笑的区别(深入) 《月亮上的足迹》主要内容是什么人教版第十九课《月亮上的足迹》的主要内容!200字的 怎样感悟人生 初中三年的语法总结. 月亮上的足迹中第九至十三段主要内容是什么 作文:一份满意的答卷.要记叙文~600字 分析比较范进中举和孔乙己的性格,社会背景等相同或不同之处500字以上 钓鱼岛为什么现在由日本控制? 人生感悟,感悟人生很早以前就像建一个这样一个群——现在终于有机会了 (我终于是太阳级别的了) 本人本着宁缺勿滥的原则.入围条件:1.对人生有一定的阅历.2.有自己的人生感悟.3.需要您 要怎么复述《孔乙己》这篇文章 和 《变色龙》 钓鱼岛现在什么情况? 人教版八年级语文 上 所有课文题目 如何让感悟人生 现在钓鱼岛是属于谁的?RT,现在都被搞晕了. 英语翻译通俗一点,我马上要去上学了,由于是刚开学,所以暂时我还没有工具书. 唐太宗,武则天的小故事 现在钓鱼岛问题怎么样了 母爱是一缕灿烂的阳光,温暖我的心田.写仿句 唐太宗和武则天~唐太宗用人唯才,起用_______、________等贤臣辅政;在中央政府设______,重视吏治,使政治清明;派兵平定北边的_____,令吐蕃等国共尊太宗为『______』.太宗的文治、武功甚为兴旺, 钓鱼岛是谁的 作文:一份满意的答卷 .求创新跪求,急. 2010五年级阅读年选读后感500字左右 什么电影适合写观后感 作文《一份满意的答卷》. 2010初中审美卷下阅读年选的读后感 六年级英语短片作文 电影观后感100词以内,原创啊,带译文。 作文 一份满意的答卷 600字给点提示也行 能不能变成叙事文啊 2010寒假阅读年选读后感征文 初中三年过的快吗 郑成功收复台湾前的故事!要之前的故事,不要后来的!要白话文! 跪求2010初中审美卷下阅读年选的读后感 总觉得时间过得特别的快做什么事 都感觉时间不够 有时候 这件事没有干完 就要去干那件事了 而且我 总爱胡思乱想 (没有那些内容 ,思想没那么肮脏) 就是想以后 干什么去 学习不好怎么
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘