Flux用来解决React在结构上的问题,从Flux也衍生了Redux,Flux也类似MVC这一类的架构,Flux最大特点就是单向数据流动
1.基本概念
这是Flux在GitHub上文档中所提供的流程展示图,里面有四个核心的东西
Action:动作,视图层发出的动作,类似事件
Dispatcher:派发器,接受Action,全局唯一,相当于C
Store:存储层,相当于M,数据变化,会让View层对应修改,负责存储和处理数据相关逻辑
View:视图层,相当于V
整个流程如下
1.从用户操作页面开始,发出Action
2.Dispatcher接受Action,然后派发Action,找到对应的Store
3.Store进行更新,发出一个change事件
4.页面接受change,更新界面
这就是一个完整单向的数据流动过程,任何过程都不会发生双向数据流动
2.Action
整个过程从Action开始,Action是一个JS对象,用来描述我们要干什么,需要什么样的数据,可以写一个Action.js文件,在这个文件里,对所有Action进行汇总,Flux只要加一个新的功能,就要对应加一个Action类型,这就像MVC中如果要添加新的功能添加新的Controller一样,在Flux里,就是添加新的Action.
export const addNum = (result) => {
AppDispatcher.dispatch({
actionName: "add",
counterCaption: result
});
};
定一个对外使用的函数,这个函数目的就是点击按钮+1,通过页面点击事件发出的Action而,触发事件的View,还是正常绑定事件,只是在对应事件的具体实现,需要找到Action里对应的函数,然后进行调用
import * as Actions from '../Actions.js';
class Son extends Component {
onClickAddButton() {
Actions.addNum("add");
}
}
当然我简化了整个过程,这样就完成了事件的触发,接下来开始执行回调函数里面的内容了,首先我们引入了AppDispatcher.js文件,它创造了一个唯一的Dispatcher对象,类似于单例,通过它来发送和接管Action.
AppDispatcher.dispatch相当于发送了一个消息,这个消息包含了需要发送的内容,也就是需要传的值,一般都会有actionName这样的字段,标明当前Action代表了什么样的操作,名随便,然后其他字段可以对应要传的值.
3.Dispatcher
在代码里,Dispatcher对应的AppDispatcher.js不需要写什么,就返回一个默认的Dispatcher对象
import {Dispatcher} from 'flux';
export default new Dispatcher();
Dispatcher提供的API也不是很多,一共5个方法
1.dispatch:用来发送消息,包含要传的数据,Action的参数
2.register(function callback):注册一个接受消息的接收器,可以接受到dispatch发送的消息,Stores自己需求,完成对应数据的操作
3.unregister:跟注册相应的方法,移除毁掉函数
4.isDispatching:boolean类型的返回值,是否目前正在调用的dispatcher
5.waitFor(array
注册这部分代码要写在Stores
import AppDispatcher from '../AppDispatcher.js';
CounterStore.dispatchToken = AppDispatcher.register((action) => {
if (action.actionName === "add") {
counterValues[action.counterCaption] ++;
} else if (action.actionName === "sub") {
counterValues[action.counterCaption] --;
}
});
CounterStore是一个计数器类,添加了dispatchToken用来保存注册的监听器,action用来接收发射器传递的数据,根据对应的action事件类型进行对应的操作,而且这个文件如果被其他文件inport之后,可以通过CounterStore.dispatchToken找到对应的监听器,这样就能用在waitFor这个方法里了
4.Store
Store用来存储应用对应状态,根据接收dispatch对应的状态,更改View的显示状态,而监听整个变化的过程就需要使用EventEmitter来帮助实现.EventEmitter的核心功能就是事件触发和事件监听.
import {EventEmitter} from 'events';
const CounterStore = Object.assign({}, EventEmitter.prototype, {
getCounterValues: function() {
return counterValues;
},
emitChange: function() {
this.emit('changed');
},
addChangeListener: function(callback) {
this.on('changed', callback);
},
removeChangeListener: function(callback) {
this.removeListener('changed', callback);
}
});
引完库文件之后,通过代码拷贝创建了一个CounterStore对象,而拷贝的内容就是EventEmitter.prototype,这样就可以使用prototype提供的响应方法.
1.getCounterValues函数是为了获取当前计数器初始值的,这个跟EventEmitter无关.
2.emitChange:这个函数调用了emit,这是EventEmitter提供的功能,用来广播一个特定事件,第一个参数是事件名称,字符串.这个方法是一个做汇总的模块用的.
3.addChangeListener:添加监听,用EventEmitter提供的on进行绑定,第一个参数是事件名称,第二个参数是对应的回调函数,这个函数将在View界面传过来
4.removeChangeListener:移除,跟on功能想法,但是删除对应的回调函数要和添加的时候是同一个,这跟JQ移除事件的要求是相同的
5.View
通过了Store,就要通过View来具体呈现,
import CounterStore from '../stores/CounterStore.js';
class Son extends Component {
componentDidMount() {
CounterStore.addChangeListener(this.onChange);
}
componentWillUnmount() {
CounterStore.removeChangeListener(this.onChange);
}
onChange() {
// 更改数据内容
}
}
在装载和摧毁两个周期调用监听的函数,同时设置回调函数onChange,onChange里就对应了对数据的操作.在View的时候,只能使用Store的get方法,而Store也没有set方法就是为了防止V直接去修改M上的数据,如果想改Store的数据,可以在发送action,重新一个新的流程,避免了传统MVC数据上的混乱
整个过程从点击按钮开始,到最后数字+1结束,完成了一个闭合的操作