react (五)Flux小结(内含MVC小知识)

来源:互联网 发布:wampserver无法打开php 编辑:程序博客网 时间:2024/06/06 03:53

从MVC到Flux

一、 MVC框架

把应用分为三个部分:

  • Model(模型):负责管理数据
  • View(视图):负责渲染用户界面
  • Controller(控制器):赋值接受用户输入,根据用户输入调用对应的Model部分逻辑,把产生的数据结果交给View部分呢,让View渲染出必要的输出。

缺点:无法禁绝View和Model之间的直接对话。

二、Flux框架


优点:

通过设置规则,即如果要改变界面,必须改变Store中的状态,如果要改变Store中的状态,必须派发一个action对象,规则禁绝了数据流混乱的可能。


Flux与MVC的对应关系:(左:flux,右:MVC)

view =》 view
dispatcher =》 controller
store =》 model
action =》 请求


缺点:

1、Store之间依赖关系。

当两个Store之间有逻辑依赖关系,就必须用上Dispatcher的waitFor函数。
另外,每一个Store是通过register函数的返回值dispatchToken标志的。
某些Store必须要等待其他Store处理完action后,才能进行操作。

2、难以进行服务器渲染

facebook设计flux时,就不是用作服务器端渲染的。

3、Store混杂了逻辑和状态。

做不到动态替换一个Store的逻辑。但是Redux可以做到。


三、Flux框架实例应用(修改前面的案例为flux框架应用)

首先,需要使用npm安装一下flux,此处不赘述了。

  • 第一步:创建Dispathcer类,作用:用于派发action。

实例化一个dispatcher实例,进行action分发操作、store注册操作等。

import {Dispatcher} from 'flux';export default new Dispatcher();
  • 第二步:action配置,通常包括两小步:
    • 首先,定义ActionTypes.js文件,用于定义对象的多种类型type,通常为字符串。
    • 其次,定义Actions.js文件,用于定义action的多个构造函数,在构造函数内部,会通过dispatcher实例对象提供的dispatch函数派发出去。
      【注意:此文件中,每一个构造函数并不是action对象本身,而是能够产生并派发action对象的函数。】
/*ActionTypes.js文件内容*/export const INCREMENT ='increment';export const DECREMENT ='decrement';
/*Actions.js文件内容*//*当文件有多个使用export导出时,可以用此语法将所有导出融为一个对象进行访问。*/import * as ActionTypes from './ActionTypes.js';import AppDispatcher from './AppDispatcher.js';export const increment =(counterCaption)=>{    AppDispatcher.dispatch({        type:ActionTypes.INCREMENT,        counterCaption:counterCaption    });};export const decrement=(counterCaption)=>{    AppDispatcher.dispatch({        type:ActionTypes.DECREMENT,        counterCaption:counterCaption    });};
  • 第三步:定义不同组件的不同store对象,用于存储应用状态,同时还要接受Dispatcher派发的动作,根据动作来决定是否要更新应用状态。

Tips:由于store会有多个,所以单独建立了一个stores文件夹进行存储为好。

注意:虽然名为store,但并不表示一个Store必须要存储,Store只是提供获取数据的方法,二Store提供的数据完全可以另一个Store计算得来。

/*CounterStore.js文件内容 */import AppDispatcher from '../AppDispatcher';import * as ActionTypes from '../ActionTypes';const counterValues={    'First':0,    'Second':10,    'Third':30};// 1、使用消息的方式建立Store和View的联系,相当于CounterStore成了EventEmitter实例对象。支持了emit、on、removeListener函数。const CounterStore=Object.assign({},EventEmitter.prototype,{    getCounterValues:function(){        return counterValues;    },    emitChange:function(){        this.emit(CHANGE_EVENT);    },    addChangeListener:function(callback){        this.on(CHANGE_EVENT,callback);    },    removeChangeListener:function(callback){        this.removeListener(CHANGE_EVENT,callback);    }});// 2、store只有注册到Dispatcher实例上才能真正发挥作用// 3、回调函数  作用:根据action对象来决定如何更新自己的状态//            其中的形参action:是派发给dispatcher的action对象(第三步中的操作)CounterStore.dispatchToken=AppDispatcher.register((action)=>{    if(action.type===ActionTypes.INCREMENT){        counterValues[action.counterCaption]++;        CounterStore.emitChange();    }else if(action.type===ActionTypes.DECREMENT){        counterValues[action.counterCaption]--;        CounterStore.emitChange();    }});export default CounterStore;

展示总和的组件Summary单独建立自己的store文件,SummaryStore.js。

import AppDispatcher from '../AppDispatcher';import * as ActionTypes from '../ActionTypes';import CounterStore from './CounterStore';function computeSummary(counterValues){    let summary=0;    for(const key in counterValues){        if(counterValues.hasOwnProperty(key)){            summary+=counterValues[key];        }    }    return summary;}const SummaryStore=Object.assign({},EventEmitter.prototype,{    getSummary:function(){        // 实时读取CounterStore里面的值        return computeSummary(CounterStore.getCounterValues());    },    emitChange:function(){        this.emit(CHANGE_EVENT);    },    addChangeListener:function(callback){        this.on(CHANGE_EVENT,callback);    },    removeChangeListener:function(callback){        this.removeListener(CHANGE_EVENT,callback);    }});SummaryStore.dispatchToken=AppDispatcher.register((action)=>{    if((action.type===ActionTypes.INCREMENT)||(action.type===ActionTypes.DECREMENT)){        //dispatchToken的用处:        // waitFor函数:告诉Dispatcher,当前的处理必须要暂停,直到dispatchToken代表的那些已注册回调函数执行结束才能继续。    AppDispatcher.waitFor([CounterStore.dispatchToken]);    SummaryStore.emitChange();}});
  • 第四步:配置Counter.js组件文件内容(相当于view视图的配置)
    Tips:会有多个视图(组件),所以建议建立view文件夹进行存储。
import React,{Component} from 'react';// 注意:react最新版本已经将proptypes独立出来,需要自行安装prop-types才可以使用。import PropTypes from 'prop-types';import CounterStore from '../stores/CounterStore';import * as Actions from '../Actions';export default class Counter extends Component{    constructor(props){        super(props);        this.onJia=this.onJia.bind(this);        this.onJian=this.onJian.bind(this);        this.state={            // 实时获取到存储的值            counter:CounterStore.getCounterValues()[props.caption]        }    }    componentDidMount(){        // 在装载完成后,给store添加事件        CounterStore.addChangeListener(this.onChange);    }    componentWillUnmount(){        // 在卸载前,删除store事件        CounterStore.removeChangeListener(this.onChange);    }    // 定义事件函数    onChange(){        // 实时从store中获取到新值 赋值给count        const newCount=CounterStore.getCounterValues()[this.props.caption];        this.setState({count:newCount});    }    onJia(){        // this.updateCount(true);        // 分发一个action        Actions.increment(this.props.caption);    }    onJian(){        // this.updateCount(false);        // 分发一个action        Actions.decrement(this.props.caption);    }    render(){        const btnStyle={            margin:"0 20px"        };        return (            <div>                <button onClick={this.onJia} style={btnStyle}>+</button>                <span>{this.props.caption}:{this.state.counter}</span>                <button onClick={this.onJian} style={btnStyle}>-</button>            </div>        )    }}Counter.propTypes={    caption:PropTypes.string.isRequired,    initValue:PropTypes.number,    onUpdate:PropTypes.func};Counter.defaultProps={    initValue:0,    onUpdate:f=>f};
原创粉丝点击