Flux框架

Flux 简介

在Flux的理念里,如果要改变界面,必须改变 Store 中的状态,如果要改变 Store
的状态,必须派发一个 action 对象,这就是规矩。

Facebook 用 Flux 框架代替原有的 MVC 框架,他们提出的 Flux 框架大致结构如下:

image

一个 Flux 应用包含四个部分,我们先粗略了解一下:

  • Dispatcher,处理动作分发,维持 Store之间的依赖关系;
  • Store ,负责存储数据和处理数据相关逻辑;
  • Action ,驱动 Dispatcher 的 JavaScript 对象;
  • View ,视图部分,负责显示用户界面。

如果非要把 Flux 和 MVC 做一个结构对比,那么, Flux的 Dispatcher 相当于 MVC
的Controller, Flux的 Store 相当于 MVC 的 Model, Flux的 View 当然就对应 MVC 的View
了,至于多出来的这个 Action ,可以理解为对应给 MVC 框架的用户请求。

Flux 应用

为了使用 Flux,首先通过命令行在项目目录下安装 Flux:

1
npm install --save flux
1. Dispatcher

几乎所有应用都只需要拥有一个Dispatcher,在src/AppDispatcher.js 中,我们创造这个唯
Dispatcher 对象,代码如下:

1
2
3
import {Dispatcher} from 'flux';

export default new Dispatcher();

在其他代码中,将会引用这个全局唯一的 Dispatcher 对象。Dispatcher 存在的作用,就是用来派发 action ,接下来我们就来定义应用中涉及的 action。

2. Action

action 顾名思义代表一个“动作”,不过这个动作只是一个普通的 JavaScript 对象,代
表一个动作的纯数据,类似于 DOMAPI 中的事件( event)。

定义 action 通常需要两个文件,一个定义 action 的类型,一个定义 action 的构造函数。

分成两个文件的主要原因是在 Store 中会根据 action 类型做
不同操作,也就有单独导入 action 类型的需要。

src/ActionTypes.js中,我们定义action的类型,代码如下:

1
2
3
export const INCREMENT = 'increment';

export const DECREMENT = 'decrement';

在这个例子中,用户只能做两个动作,一个是点击“+”按钮, 一个是点击“-”按
钮,所以我们只有两个 action 类型INCREMENT和 DECREMENT。

现在我们在src/Action.js 文件中定义 action 构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
});
};
3. Store

一个 Store 也是一个对象,这个对象存储应用状态,同时还要接受 Dispatcher 派发的
动作,根据动作来决定是否要更新应用状态。

我们创造两个 Store ,一个是为 Counter 组件服务的 CounterStore ,另一个就是为总
数服务的 SummaryStore

我们首先添加CounterStore,放在src/stores/CounterStore.js文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const counterValues = {
'First': 0,
'Second': 10,
'Third': 30
};


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

});

当Store 的状态发生变化的时候, 需要通知应用的其他部分做必要的响应,而做出响应的部分当然就是 View 部分,我们用消息的方式建立 Store 和 View 的联系。

  • emit 函数,可以广播一个特定 件,第一个参数是字符串类型的事件名称;
  • on 函数,可以增加一个挂在这个EventEmitter对象特定事件上的处理函数,第
    一个参数是字符串类型的 事件名称,第二个参数是处理函数;
  • removeListener 函数,和on 函数做的事情相反, 删除挂在这个 Eve ntEmitter 对象特定事件上的处理函数。

CounterStore 函数还提供一个 getCounterValues 函数,用于让应用中其他模块可以读取当
前的计数值,当前的计数值存储在文件模块级的变量 counterValues中。

上面实现的 Store 只有注册到 Dispatcher实例上才能真 正发挥作用,所以还需要增
下列代码:

1
2
3
4
5
6
7
8
9
10
import AppDispatcher from '../AppDispatcher.js';
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();
}
});

这是最重要的一个步骤,要把CounterStore注册到全局唯一的Dispatcher上去。

接下来,我们再来看看另一个 Store ,也就是代表所有计数器计数值总和的 Store ,在src/stores/SummaryStore.js中。
这几个函数的代码和 CounterStore 中完全重复,
不同点是对获取状态函数的定义,SummaryStore在 Dispatcher 上注册的回调函数也和 CounterStore 很不一样。

4. View

存在于 Flux 框架中的 React 组件需要实现以下几个功能:

  • 创建时要读取 Store 上的状态来初始化组件内部状态;
  • 当Store上状态发生变化时,组件要立刻同步更新内部状态保持一致;
  • View 如果要改变 Store 状态,必须而且只能派发 action。
------ 本文结束 ------
0%