Flux是Facebook官方使用的前端MVC框架,在React的基础上构建。在React 数据流与Flux框架中介绍Flux时,使用了一个实现简单搜索功能的示例,本篇将介绍这个示例的实现过程。
1. 简介
在这个项目中,我们使用CommonJS规范的Node.js模块系统。所依赖的模块已在package.json中进行管理,如果你对Node.js有民了解,将更容易理解这个示例结构。本示例项目完整代码请查看:flux-demo,项目下载后按以下步骤操作:
npm install,安装所需要模块npm run build,构建项目npm start,watchify监视文件修改并实时更新
执行上面步骤1、2后即可点击index.html查看运行效果。运行步骤3可以编写代码的同时实时查看执行效果,在开发过程中建议运行此过程调试。
源码结构
本例中,项目结构如下:
flux-demo
|
+ css // 样式文件,本例中bootstrap样式
+ js
|
+ actions
+ components
+ constants
+ dispatcher
+ stores
+ app.js
+ index.html //示例启动页面
2. 创建分发器Dispatcher
Dispatcher是一个调度中心,Dispatcher只能有一个,负责分发正确的Action路径,并将其转发到已注册的Store处理逻辑中。
var Dispatcher = require('flux').Dispatcher;
module.exports = new Dispatcher();
Dispatcher的具体用处,请结合本例的FluxAction.js与FluxStore.js两个文件理解。
3. 创建Store
我们可以使用Node.js的EventEmitter模块来启动一个视图。在本例的controller-views(控制视图)中,通过EventEmitter模块来发送视图'change'事件:
'use strict';
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
}
});
module.exports = FluxStore;
Store用于实现主要的业务逻辑。在本例中,Store实现了监听视图变化、实现搜索功能、注册回调函数等功能。
4. 创建视图View
在Flux框架中,视图被分为了两类:controller-view控制视图和其它视图。
4.1 controller-view
在本例中,controller-view是FluxApp.js。它会做两件事司:加载其它子组件、监听'change'事件并传递给子组件:
/**
* 这个组件是顶层的 "Controller-View"
* 它会监听 FluxStore 的'change'事件,并将新数据传递给子组件
*/
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());
}
});
module.exports = FluxApp;
controller-view会在app.js中调用,而app.js是本例入口点(index.html也是引用这个文件编译后的脚本):
/**
* Flux示例启动文件
*
*/
var React = require('react');
var ReactDOM = require('react-dom');
var FluxApp = require('./components/FluxApp');
ReactDOM.render(
<FluxApp />,
document.getElementById('fluxapp')
);
4.2 其它View
在本例中,除controller-view还有Footer.js、MainSection.js、ResultItem.js、SearchInput.js4个子组件,它们会根据主控视图FluxApp.js在不同情况。
Footer.js-显示页面页脚内容MainSection.js-主页面组件ResultItem.js-搜索结果加载组SearchInput.js-搜索输入组件
5. 创建Action
Action用于动作(事件)事件的捕获,同时会接收要传递的数据。本例中,只用到了search一个动作,该方法用于捕获搜索逻辑:
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
});
}
};
module.exports = FluxAction;
在上面示例中,引用了AppDispatcher分发器,它会通过dispatch方法以将指定的actionType分发到FluxStore.js中通过AppDispatcher注册的业务处理逻辑。
在上例中,还使用了通过keymirror模块定义的运作常量,如果动作类型较少也可以自己定义actionType常量。
6. 运行
在index.html中,引用了以下脚本:
<script src="js/bundle.min.js"></script>
我们并没有上传这个文件,编写完所有功能模块后,可以运行npm run build命令来生成这个文件。该文件由Browserify打包并编译npm模块在浏览器中的运行环境。
命令执行完成后,会生成js/bundle.min.js文件,生成完毕后即可打开index.html查看运行效果。
