淺說Flux開發
淺說Flux開發
前段時間,寫了篇關于React的文件:React:組件的生命周期,比較詳細的說了下React組件的生命周期。
說道 React,很容易可以聯想到 Flux。今天以 React 介紹及實踐教程 一文中的demo為示例,簡單說說 Flux 的開發方式。
Flux是什么
Flux 是 非死book 用戶建立客戶端 Web 應用的前端架構, 它通過利用一個單向的數據流補充了 React 的組合視圖組件。
Flux 的設計架構如圖:

Flux 架構有三個主要部分:Dispatcher 、Store 和View(React 組件)。Store 包含了應用的所有數據,Dispatcher 負責調度,替換了 MVC中 的Controller,當 Action 觸發時,決定了 Store 如何更新。當 Store變化后,View 同時被更新,還可以生成一個由Dispatcher 處理的Action。這確保了數據在系統組件間單向流動。當系統有多個 Store 和 View 時,仍可視為只有一個 Store 和一個 View,因為數據只朝一個方向流動,并且不同的 Store 和 View 之間不會直接影響彼此。
Flux的簡單開發
在顏色盤的demo中,對界面的組件劃分是這樣的:

用戶在 ColorPanel(View) 上產生一個 Action,Action 將數據流轉到 Dispatcher,Dispatcher 根據 Action 傳遞的數據更新 Store,當 Store變化后,View 同時被更新。
(若你要獲取本文的源代碼,可以戳此:Flux-Demo)
Store
Store 包含了應用的所有數據,并負責更新 View(ColorPanel),為簡單,簡化 Store 的定義:
let EventEmitter = require('events').EventEmitter;
let emitter = new EventEmitter();
export let colorStore = {
colorId: 1,
listenChange(callback){
emitter.on('colorChange', callback);
},
getColorId(){
return this.colorId
},
setColorId(colorId){
this.colorId = colorId;
emitter.emit('colorChange');
}
}; Dispatcher
Action 要靠 Dispatcher 去調度,才能更新 Store,并有 Store 去更新對應的 View 組件。定義一個 colorDispatcher 用于調度:
import {Dispatcher} from 'flux';
import {colorStore} from './colorStore';
export let colorDispatcher = new Dispatcher();
colorDispatcher.register((payload) => { switch (payload.action){ case 'CHANGE_COLOR_ID': colorStore.setColorId(payload.colorId); break; } }); Action
當鼠標懸浮在 ColorBar 中的一個顏色框時,根據顏色框 ID 的變化,ColorDisplay 顯示對應的顏色。我們定義一個 Action 為 CHANGE_COLOR_ID:
import {colorDispatcher} from './colorDispatcher';
export let colorAction = {
changeColorId(colorId){
colorDispatcher.dispatch({
action: 'CHANGE_COLOR_ID',
colorId: colorId
})
}
}; 建議定義的 Action 符合 FSA 規范。
View
按照上圖的組件劃分,我們渲染出界面:
ColorDisplay:
import React, {Component} from 'react';
class ColorDisplay extends Component {
shouldComponentUpdate(nextProps, nextState){
return this.props.selectedColor.id !== nextProps.selectedColor.id;
}
render(){
return (
<div className = 'color-display'>
<div className = {this.props.selectedColor.value}>
{this.props.selectedColor.title}
</div>
</div>
)
}
}
export default ColorDisplay; ColorBar:
import React, {Component} from 'react';
import {colorAction} from '../flux/colorAction';
import {colorStore} from '../flux/colorStore';
class ColorBar extends Component {
handleHover(colorId){
let preColorId = colorStore.getColorId();
if(preColorId != colorId){
colorAction.changeColorId(colorId);
}
}
render(){
return (
<ul>
{/* Reaact中循環渲染元素時, 需要用key屬性確保正確渲染, key值唯一*/}
{this.props.colors.map(function(color){
return (
<li key = {color.id}
onMouseOver = {this.handleHover.bind(this, color.id)}
className = {color.value}>
</li>
)
}, this)}
</ul>
)
}
}
export default ColorBar; ColorPanel:
import React, {Component} from 'react';
import ColorDisplay from './colorDisplay';
import ColorBar from './colorBar';
import {colorStore} from '../flux/colorStore'
class ColorPanel extends Component {
constructor(props){
super(props);
this.state = {
selectedColor: this.getSelectedColor(colorStore.getColorId())
};
colorStore.listenChange(this.onColorHover.bind(this));
}
getSelectedColor(colorId){
if(! colorId){
return null
}
let length = this.props.colors.length;
let i;
for(i = 0; i < length; i++) {
if(this.props.colors[i].id === colorId){
break;
}
}
return this.props.colors[i];
}
shouldComponentUpdate(nextProps, nextState){
return this.state.selectedColor.id !== nextState.selectedColor.id;
}
render(){
return (
<div>
<ColorDisplay selectedColor = {this.state.selectedColor} />
<ColorBar colors = {this.props.colors} />
</div>
)
}
onColorHover(){
let colorId = colorStore.getColorId();
this.setState({
selectedColor: this.getSelectedColor(colorId)
})
}
}
ColorPanel.defaultProps = {
colors: [
{id: 1, value: 'red', title: 'red'},
{id: 2, value: 'blue', title: 'blue'},
{id: 3, value: 'green', title: 'green'},
{id: 4, value: 'yellow', title: 'yellow'},
{id: 5, value: 'pink', title: 'pink'},
{id: 6, value: 'black', title: 'black'}
]
};
export default ColorPanel; App:
var ReactDom = require('react-dom');
import React from 'react';
import ColorPanel from './panel/colorPanel';
require('./app.css');
window.onload = function () {
ReactDom.render(<ColorPanel />, document.getElementById('demos'));
} 最終的的界面如下:

總結
本文簡單的說了下如何利用 Flux 去開發實際應用,對于負載要求不高的應用,Flux 是完全可以使用的,復雜的富應用則可以借助 Redux + React 構建,Redux 負責數據分發和管理,其數據流的可控性比 Flux 更強,React 則依舊負責 View,但二者并不能直接連接,需要依賴 react-redux。
若你要獲取本文的源代碼,可以戳此:Flux-Demo
來自: http://blog.csdn.net/u011043843/article/details/50531610