本文摘自《Flux架构入门教程》和《谈一谈我对 React Flux 架构的理解》。也有自己的观点和总结。转载请注明出处。

一、Flux架构描述

  1. Flux是什么

  Flux是Facebook用来构建客户端Web应用的应用架构。它利用单向数据流的方式来组合React中的视图组件。它更像一个模式而不是一个正式的框架,开发者不需要太多的新代码就可以快速的上手Flux。

  简单说,Flux 是一种架构思想,专门解决软件的结构问题。

  2. 基本概念

  dispatcher

  事件调度中心,flux模型的中心枢纽,管理着Flux应用中的所有数据流。它本质上是Store的回调注册。每个Store注册它自己并提供一个回调函数。当Dispatcher响应Action时,通过已注册的回调函数,将Action提供的数据负载发送给应用中的所有Store。应用层级单例!!

  store

  负责封装应用的业务逻辑跟数据的交互。

  • Store中包含应用所有的数据

  • Store是应用中唯一的数据发生变更的地方

  • Store中没有赋值接口---所有数据变更都是由dispatcher发送到store,新的数据随着Store触发的change事件传回view。Store对外只暴露getter,不允许提供setter!!禁止在任何地方直接操作Store。

  view

  • controller-view 可以理解成MVC模型中的controller,它一般由应用的顶层容器充当,负责从store中获取数据并将数据传递到子组件中。简单的应用一般只有一个controller-view,复杂应用中也可以有多个。controller-view是应用中唯一可以操作state的地方(setState())

  • view(UI组件) ui-component 职责单一只允许调用action触发事件,数据从由上层容器通过属性传递过来。

  其他

  action creators 作为dispatcher的辅助函数,通常可以认为是Flux中的第四部分。ActionCreators是相对独立的,它作为语法上的辅助函数以action的形式使得dispatcher传递数据更为便利。

  简单讲,Flux将一个应用分成四个部分。

  • View: 视图层
  • Action(动作):视图层发出的消息(比如mouseClick)
  • Dispatcher(派发器):用来接收Actions、执行回调函数
  • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

  

  Flux 的最大特点,就是数据的"单向流动"。

  1. 用户访问 View
  2. View 发出用户的 Action
  3. Dispatcher 收到 Action,要求 Store 进行相应的更新
  4. Store 更新后,发出一个"change"事件
  5. View 收到"change"事件后,更新页面

二、通过代码去理解Flux 

  1. 安装demo  

  为了便于讲解,我写了一个Demo(摘自阮一峰)。请先安装一下。


$ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git
$ cd extremely-simple-flux-demo && npm install
$ npm start

  然后,访问 http://127.0.0.1:8080

  

  2. index.jsx

  请打开 Demo 的index.jsx ,你会看到只加载了一个组件。

// index.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var MyButtonController = require('./components/MyButtonController'); ReactDOM.render(
<MyButtonController/>,
document.querySelector('#example')
);

  上面代码中,你可能注意到了,组件的名字不是 MyButton,而是 MyButtonController。这是为什么?

  这里,我采用的是 React 的 controller view 模式。"controller view"组件只用来保存状态,然后将其转发给子组件。

  3. MyButtonController.jsx

// components/MyButtonController.jsx
var React = require('react');
var ButtonActions = require('../actions/ButtonActions');
var MyButton = require('./MyButton'); var MyButtonController = React.createClass({
createNewItem: function (event) {
ButtonActions.addNewItem('new item');
}, render: function() {
return <MyButton onClick={this.createNewItem} />;
}
}); module.exports = MyButtonController;

  上面代码中,MyButtonController将参数传给子组件MyButton.

  4. MyButton.jsx

// components/MyButton.jsx
var React = require('react'); var MyButton = function(props) {
return <div>
<button onClick={props.onClick}>New Item</button>
</div>;
}; module.exports = MyButton;

  上面代码中,你可以看到MyButton是一个纯组件(即不含有任何状态),从而方便了测试和复用。这就是"controll view"模式的最大优点。

  MyButton只有一个逻辑,就是一旦用户点击,就调用this.createNewItem方法,向Dispatcher发出一个Action。

  5. ButtonActions.js

  每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)。在这个Demo里面,ButtonActions对象用于存放所有的Action。

// actions/ButtonActions.js
var AppDispatcher = require('../dispatcher/AppDispatcher'); var ButtonActions = {
addNewItem: function (text) {
AppDispatcher.dispatch({
actionType: 'ADD_NEW_ITEM',
text: text
});
},
};

  上面代码中,ButtonActions.addNewItem方法使用AppDispatcher,把动作ADD_NEW_ITEM派发到Store。

  6. AppDispatcher.js

  Dispatcher 的作用是将 Action 派发到 Store。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。

  Facebook官方的Dispatcher输出一个类,你要写一个AppDispatcher.js,生成 Dispatcher 实例。


// dispatcher/AppDispatcher.js
var Dispatcher = require('flux').Dispatcher;
var AppDispatcher = new Dispatcher();
var ListStore = require('../stores/ListStore'); AppDispatcher.register(function (action) {
switch(action.actionType) {
case 'ADD_NEW_ITEM':
ListStore.addNewItemHandler(action.text);
ListStore.emitChange();
break;
default:
// no op
}
})

  AppDispatcher.register()方法用来登记各种Action的回调函数。

  上面代码中,Dispatcher收到ADD_NEW_ITEM动作,就会执行回调函数,对ListStore进行操作。记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。

  7. ListStore.js

  Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。在我们的 Demo 中,有一个ListStore,所有数据都存放在那里。

  下面代码中,ListStore.items用来保存条目,ListStore.getAll()用来读取所有条目,ListStore.emitChange()用来发出一个"change"事件。

  由于 Store 需要在变动后向 View 发送"change"事件,因此它必须实现事件接口。

// stores/ListStore.js
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign'); var ListStore = assign({}, EventEmitter.prototype, {
items: [], getAll: function () {
return this.items;
}, addNewItemHandler: function (text) {
this.items.push(text);
}, emitChange: function () {
this.emit('change');
}, addChangeListener: function(callback) {
this.on('change', callback);
}, removeChangeListener: function(callback) {
this.removeListener('change', callback);
}
});

  上面代码中,ListStore继承了EventEmitter.prototype,因此就能使用ListStore.on()和ListStore.emit(),来监听和触发事件了。

  Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。

  8. 修改MyButtonController.jsx

  现在,我们再回过头来修改View,让它监听 Store 的 change 事件。

// components/MyButtonController.jsx
var React = require('react');
var ListStore = require('../stores/ListStore');
var ButtonActions = require('../actions/ButtonActions');
var MyButton = require('./MyButton'); var MyButtonController = React.createClass({
getInitialState: function () {
return {
items: ListStore.getAll()
};
}, componentDidMount: function() {
ListStore.addChangeListener(this._onChange);
}, componentWillUnmount: function() {
ListStore.removeChangeListener(this._onChange);
}, _onChange: function () {
this.setState({
items: ListStore.getAll()
});
}, createNewItem: function (event) {
ButtonActions.addNewItem('new item');
}, render: function() {
return <MyButton
items={this.state.items}
onClick={this.createNewItem}
/>;
}
});

 上面代码中,你可以看到当MyButtonController 发现 Store 发出 change 事件,就会调用 this._onChange 更新组件状态,从而触发重新渲染。

三、这些代码是如何工作的

  我对这个Demo的结构及流程画了张图:

  

  官方讲解流程:

  

理解Flux架构的更多相关文章

  1. Flux架构

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

  2. 理解RESTful架构

    越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency).高 ...

  3. [转]理解RESTful架构

    原文地址:http://www.ruanyifeng.com/blog/2011/09/restful 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件" ...

  4. 理解RESTful架构(转载)

    本文转载自:http://www.ruanyifeng.com/blog/2011/09/restful.html 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软 ...

  5. [转载] 理解RESTful架构

    原文: http://www.ruanyifeng.com/blog/2011/09/restful.html 理解RESTful架构   作者: 阮一峰 日期: 2011年9月12日 越来越多的人开 ...

  6. fw:理解RESTful架构

    理解RESTful架构   作者: 阮一峰 日期: 2011年9月12日 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立 ...

  7. 理解RESTful架构(转)

    理解RESTful架构   作者: 阮一峰 http://www.ruanyifeng.com/blog/2011/09/restful 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这 ...

  8. 【转】理解RESTful架构

    [转]理解RESTful架构 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时( ...

  9. 理解RESTful架构(转)

    越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency).高 ...

随机推荐

  1. yii page title, CMenu 中文不显示

    Page title: <?php echo CHtml::encode(iconv('gbk','utf-8',$this->pageTitle)); ?> CMenu: fram ...

  2. 线上Bug修复流程

  3. setTimeout OR setInterval?

    setTimeout 和setInterval从字面上应该是可以知道其大意的.timeout:延时:interval:间隔: 两者的区别就像是它们自己的英文解释一样:setTimeout是延时执行,并 ...

  4. Batch Norm、Layer Norm、Weight Norm与SELU

    加速网络收敛——BN.LN.WN与selu 自Batch Norm出现之后,Layer Norm和Weight Norm作为Batch Norm的变体相继出现.最近又出来一个很”简单”的激活函数Sel ...

  5. Ubuntu 添加删除用户 How to Add and Delete Users on Ubuntu 16.04

      Introduction One of the most basic tasks that you should know how to do on a fresh Linux server is ...

  6. sql 去重关键字 distinct

    单列去重: mysql: drop table test;create table test(id int(4));insert into test values(1),(2),(3),(4),(1) ...

  7. maven属性、profile、资源过滤、不同环境构建项目

    maven针对不同环境构建项目 maven使用属性.profile及资源过滤支持针对不同环境构建项目 maven属性 maven共有六类属性 1.最常见的是自定义属性,即在pom文件里通过<pr ...

  8. JS + jQuery 实现元素自动滚动到底部,兼容IE、FF、Chrome

    HTML代码: <ul class="tasklog-dialog-ul" id="auto_to_bottom"> <li>删除虚拟机 ...

  9. null的专栏:https://blog.csdn.net/google19890102

    null的专栏:https://blog.csdn.net/google19890102 csdn博客专栏:https://blog.csdn.net/column.html

  10. python是如何找到对应的package的?

    我们在写python代码或者阅读别人的代码时,可能会碰到对应module无法找到的问题,这时如何解决呢?我们如果对python解释器如何查找对应的module有比较深刻的理解,那么我们就可以轻松解决相 ...