Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浅说Flux开发 #14

Open
dwqs opened this issue Aug 3, 2016 · 0 comments
Open

浅说Flux开发 #14

dwqs opened this issue Aug 3, 2016 · 0 comments

Comments

@dwqs
Copy link
Owner

dwqs commented Aug 3, 2016

前段时间,写了篇关于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 为 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'));
}

最终的的界面如下:

react-started

总结

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant