淺說Flux開發

jopen 8年前發布 | 14K 次閱讀 前端技術

淺說Flux開發

前段時間,寫了篇關于React的文件:React:組件的生命周期,比較詳細的說了下React組件的生命周期。

說道 React,很容易可以聯想到 Flux。今天以 React 介紹及實踐教程 一文中的demo為示例,簡單說說 Flux 的開發方式。

Flux是什么

Flux 是 非死book 用戶建立客戶端 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

原文出處:http://www.ido321.com/1658.html

來自: http://blog.csdn.net/u011043843/article/details/50531610

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!