IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    浅说Flux开发

    dwqs发表于 2015-12-02 15:45:17
    love 0

    前段时间,写了篇关于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中,对界面的组件划分是这样的:

    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 为 CHANGECOLORID:

    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'));
    }
    

    最终的的界面如下:

    react-started

    总结

    本文简单的说了下如何利用 Flux 去开发实际应用,对于负载要求不高的应用,Flux 是完全可以使用的,复杂的富应用则可以借助 Redux + React 构建,Redux 负责数据分发和管理,其数据流的可控性比 Flux 更强,React 则依旧负责 View,但二者并不能直接连接,需要依赖 react-redux。

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

    转载请注明:淡忘~浅思 » 浅说Flux开发



沪ICP备19023445号-2号
友情链接