转载

浅说Flux在实际应用中的开发方式

前段时间,写了篇关于React的文件: React:组件的生命周期 ,比较详细的说了下React组件的生命周期。

说道 React ,很容易可以联想到 Flux 。今天以 React 介绍及实践教程 一文中的demo为示例,简单说说 Flux 的开发方式。

Flux是什么

Flux 是 Facebook 用户建立客户端 Web 应用的前端架构, 它通过利用一个单向的数据流补充了 React 的组合视图组件。

Flux 的设计架构如图:

浅说Flux在实际应用中的开发方式

Flux 架构有三个主要部分:Dispatcher 、Store 和View(React 组件)。Store 包含了应用的所有数据,Dispatcher 负责调度,替换了 MVC中 的Controller,当 Action 触发时,决定了 Store 如何更新。当 Store变化后,View 同时被更新,还可以生成一个由Dispatcher 处理的Action。这确保了数据在系统组件间单向流动。当系统有多个 Store 和 View 时,仍可视为只有一个 Store 和一个 View,因为数据只朝一个方向流动,并且不同的 Store 和 View 之间不会直接影响彼此。

Flux的简单开发

在颜色盘的demo中,对界面的组件划分是这样的:

浅说Flux在实际应用中的开发方式

用户在 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 去开发实际应用,对于负载要求不高的应用,Flux 是完全可以使用的,复杂的富应用则可以借助 Redux + React 构建, Redux 负责数据分发和管理,其数据流的可控性比 Flux 更强,React 则依旧负责 View,但二者并不能直接连接,需要依赖 react-redux 。

若你要获取本文的源代码,可以戳此: Flux-Demo

正文到此结束
Loading...