React 本身只是一个视图(View),也就是MVC中的V。你可以很容易将React集成到你当前使用MVC框架中,也可以使用FactBook官方推出的Flux框架。Flux框架为React 提供一套单身的数据流(Data Flow)的模式,Flux为我们提供了组织代码和安排内部逻辑的方式,这样我们只需要写很少的代码就能实现想要的功能,使应用更加易于开发和维护。
1. Flux框架
1.1 Flux框架组成
一个基于Flux框架的应用由四部分构成:Action(动作)、Dispatcher(分发器)、Store(数据存储)、View(视图)。各部分作用如下:
Action:视图层发出的动作(如:鼠标点击等),提供给Dispatcher并传递数据给StoreDispatcher:处理动作分发,接收Action,并执行相应的回调函数Store:数据交互和逻辑部分,用于存放应用的状态,状态改变后会提醒Views要更新页面View:即,React 组件,用于响应用户交互并
项层的React 组件类似于一个控制视图(Controller-Views),控制视图的组件会与Store进行交互,并传递数据给子组件。在Flux模式中,每个部分都是独立的,通过功能隔离保证了功能简单的性,使每个部分更易于测试和维护。
1.2 Flux数据流
Flux使用单向的数据流,没有双向绑定,使它很容易审查问题来源。其状态是在Store中维护的,所以状态非常易于追踪。Flux数据流可以简单表示如下:

很多时候View会通过用户交互触发Action,所以一个完整的数据流类似如下:

在上图所示的Flux流程中:
- 首选需要要一个
Action,用户在访问View时通过用户交互(如:'onClick'事件)会触发一个Action Dispatcher收到用户的Action,会将动作分发给Store,即:调用所有注册到Action上的Store回调函数Store根据接收的Action更新自身数据并触发一个'change'事件通知ViewView收到'change'事件后,根据收到的数据调用setState更新组件
在上述过程中,Flux的每个部分前一步获取输入内容并进行处理,然后向下一步发送输出内容,也就构成了单向数据流循环。这也保证了应用中的各部分分工明确、流程清晰,实现了功能的解耦。
2. View
View是React 组件,它会从Store获取状态(数据),在View中需要绑定'change'事件处理器。
一个View可能会关联到多个Store来管理不同部分的状态,这是因为在React 中更新视图非常简单只需要setState。Flux中复杂的业务逻辑都被封装到Store里面,这样View就只需要响应状态并更新UI即可(MainSection.js)。
var React = require('react');
var MainSection = require('./MainSection');
var Footer = require('./Footer');
var FluxStore = require('../stores/FluxStore');
/**
* 从FluxStore中获取当关的状态
*/
function getFluxState() {
return {
results: FluxStore.getResults(),
keyword: FluxStore.getKeyword()
};
}
var FluxApp = React.createClass({
getInitialState: function() {
return getFluxState();
},
componentDidMount: function() {
FluxStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
FluxStore.removeChangeListener(this._onChange);
},
/**
* @return {object}
*/
render: function() {
return (
<div>
<MainSection
results={this.state.results}
keyword={this.state.keyword}
/>
<Footer />
</div>
);
},
/**
* FluxStore的'change'事件处理
*/
_onChange: function() {
this.setState(getFluxState());
}
});
3. Action
Flux流程的开始首先要创建Action(动作),Action通过一些action creator方法来定义,这些方法会暴露给外部调用,并通过Dispatcher分发相应的动作。 参见(FluxAction.js):
var AppDispatcher = require('../dispatcher/AppDispatcher');
var FluxConstants = require('../constants/FluxConstants');
var FluxAction = {
/**
* @param {string} text
*/
search: function(text) {
AppDispatcher.dispatch({
actionType: FluxConstants.FLXU_SEARCH,
text: text
});
}
};
Action是用来封装传递数据的,动作只是一个简单的对象,它包含两部分:payload(数据)和 type(类型)。type是一个字符串常量,用来标识动作。
4. Dispatcher
Dispatcher是一个分发中心,它会管理所有数据的流向,并分发Action给Store。一个应用只需要一个Dispatcher分发中心,Dispatcher没有太多其他的业务逻辑(AppDispatcher.js)。
var Dispatcher = require('flux').Dispatcher;
module.exports = new Dispatcher();
5. Store
Store包含应用的状态和业务逻辑,不同的Store管理应用中不同部分的状态。
在Store注册给Dispatcher 的回调函数中会接收到分发的Action。回调函数会判断Action中的type并调用具体内部的方法处理来处理Action带来 它会更新 所有注册过回调 都会分发给所有注册的回调,所以回调函数里面要判断这个 action 的 type 并调用相关的内部方法处理更新 action 带过来的数据(payload),最后通知View数据的变更(FluxStore.js)。
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var FluxConstants = require('../constants/FluxConstants');
var assign = require('object-assign');
var CHANGE_EVENT = 'change';
var _results = {};
var _keyword = '';
// 模似一个搜索源
const _source = [
{
name:'IT笔录',
domain: 'niefengjun.cn'
},
{
name:'一介布衣',
domain: 'yijibuyi.com'
},
{
name:'老聂的小站',
domain: 'niefengjun.cn'
}
];
/**
* 实现搜索功能
* @param {string} 搜索的内容
*/
function search(text) {
_keyword = text;
_source.forEach(function(item){
if(item.name.indexOf(text)>-1){
var id = (+new Date() + Math.floor(Math.random() * 999999)).toString(36);
_results[id] = {
id: id,
name:item.name,
domain: item.domain
};
}
});
}
var FluxStore = assign({}, EventEmitter.prototype, {
/**
* 获取Flux 搜索的结果集
* @return {object}
*/
getResults: function() {
return _results;
},
/**
* 获取Flux 搜索的关键字
* @return {string}
*/
getKeyword: function() {
return _keyword;
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
/**
* @param {function} callback
*/
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
/**
* @param {function} callback
*/
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
});
// 注册回调处理函数
AppDispatcher.register(function(action) {
var text;
switch(action.actionType) {
case FluxConstants.FLXU_SEARCH:
text = action.text.trim();
if (text !== '') {
search(text);
FluxStore.emitChange();
}
break;
default:
// no op
}
});
