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
并传递数据给Store
Dispatcher
:处理动作分发,接收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'
事件通知View
View
收到'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: 'itbilu.com' }, { 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 } });