和 React 一样,Flux 同样是由 Facebook 推出的,它诞生的初衷就是为了解决 React 独立架构状态管理复杂的问题。就目前来看,Flux 并没有跟任何一个库或者框架绑定在一起,尤其是 React,只是说利用 React 作为 Flux 的 view 层会更顺手,他们之间的搭配会更融洽一些。注意,Flux 并不是一个真正的工具类库而更像是一个状态管理的架构模式,即使它实现了一个可用的工具方法。这一观念也是被 Flux 的设计工程师和社区所认可的。

Flux 模型

Flux 主要提出的是针对现有前端 MVC 框架存在的局限性而总结出来的一套基于 dispatcher 的前端应用架构模式。单向的数据和逻辑流是 Flux 的核心思想。其流程模型如下:

Flux 系列二:Flux 基础概念 - 图1

说到单向数据流,有人可能会想到 React 中的数据流也是单向的,Flux 中单向数据流正是对整个架构的延伸。从上面的模型图可以很清晰的了解到,在 Flux 应用中,数据状态统一存储在 Store 里面,不管是页面上用户的交互还是其他的交互都不能直接修改 Store 里面的数据状态,中间必须要经过 Dispatcher,Dispatcher 是整个架构中唯一一个可以改变 Store 数据状态的逻辑。View 会订阅 Store 里面的数据,当 Store 里面的数据状态发生变化后,View 会响应 Store 数据的变化,然后利用新的 Store 数据渲染页面,到这里就是 React 的工作了。

说了这么多,很多人可能还很懵,特别是对于之前没有接触过这方面知识的人来说, Action? Dispatcher? Store? 他们到底是怎样的存在?

为了解除大家心中的疑惑,下面将对单个部分进行详细的讲解,让大家对整个 Flux 模式更加了解。

Flux 概念

一个 Flux 应用主要由 Action、Dispatcher、Store 和 View 组成,其中 Action 负责描述数据的变化,Dispatcher 负责接受 Action 描述的数据变化,同时分发事件,Store 负责存储数据并响应事件、更新数据,View 则负责订阅 Store 中的数据并利用这些数据渲染页面。

Flux 系列二:Flux 基础概念 - 图2

Flux 架构模式看上去是不是很像 MVC 呢?但是相比 MVC ,Flux 并没有一个职责明确的 Controller,而是有个将 View 和 Store 进行绑定的 Controller-View 层,但是其中的逻辑并没有 MVC 中 Controller 层逻辑来的复杂。关于 Flux 架构模式和 MVC 的区别,我们将在下一小节 Flux 与 MVC 中进行详细的介绍。

Action

在 Flux 架构模式中,Action 只是一个简单的 JavaScript 对象,一般会包含 type、payload 等属性,主要用来描述需要对 Store 里面的数据状态做怎样的改变,但是它不直接对 Store 进行修改。Action 对象形如:

  1. {
  2. type: 'ADD_NEW_ITEM',
  3. text: 'JavaScript'
  4. }

这个对象描述了 Store 里面的数据状态发生更新的类型是 “ADD_NEW_ITEM”,更新的属性值是 “ text: ‘JavaScript’ ”。在 Action 里面除了这两个属性外,还可以包含更多的信息,比如添加 item 的 ID、类型等等。

在开源社区中,有一套关于 Flux 中 Action 对象定义的规范,被称为 FSA(Flux Standard Action)。该规范定义了一个 Flux Action 必须包含一个 type 属性,可以拥有 error、payload 或者 meta 属性。除此之外,不能包含其他额外的属性。

Dispatcher

Dispatcher 算是 Flux 架构模式的核心了,它就像 CPU 一样,负责在 View 和 Store 之间建立起 Action 的正确传递路线。其实质作用是将 Action 派发到 Store。特别注意的是 Dispatcher 只能有一个,而且是全局的。

Facebook 官方关于 Dispatcher 的实现 输出一个类,你需要写一个 AppDispatcher.js 生成 Dispatcher 的实例,以便在其他的业务逻辑代码中进行引用。

  1. // dispatcher/AppDispatcher.js
  2. const Dispatcher = require('flux').Dispatcher;
  3. module.exports = new Dispatcher();

在 Dispatcher 实例上有个 register 方法,专门用来注册 Action 的回调函数,形如:

  1. // dispatcher/AppDispatcher.js
  2. AppDispatcher.register(function (action) {
  3. switch(action.actionType) {
  4. case 'ADD_NEW_ITEM':
  5. // do something
  6. break;
  7. default:
  8. // do something
  9. }
  10. })

代码实例中,展示了 Dispatcher 收到一个 type 为 ADD_NEW_ITEM 的动作后,就会执行相应的逻辑处理,逻辑里面一般是对 Store 里面的数据状态的操作。

记住,Dispatcher 只用来派发 Action,不应该有其他的处理逻辑。

Store

Store 就是用来负责保存整个应用的数据状态的,并且定义了一些数据修改的逻辑。当 Dispatcher 分发了一个 Action 后,就会调用 Store 里面修改数据逻辑的方法,并以 Action 为参数。

Store 里面修改数据逻辑的方法会根据传进来的 Action 对象里面的 type 判断是否要对 Store 的数据做处理或者做什么样的逻辑处理。在完成数据更新后,会触发一个更新事件。

需要特别注意的是,在 Flux 里面,Store 对外暴露的只有 getter(读取器),并不会有 setter(设置器)。这意味着,在 Store 之外,只能获取 Store 里面的数据状态而不能对其进行设置更改。

  1. // stores/ListStore.js
  2. const EventEmitter = require('events').EventEmitter;
  3. const assign = require('object-assign');
  4. const ListStore = assign({}, EventEmitter.prototype, {
  5. // 存储应用的数据
  6. items: [],
  7. // 获取 Store 里面所有的数据
  8. getAll: function () {
  9. return this.items;
  10. },
  11. // 向 Store 里面添加数据
  12. addNewItemHandler: function (text) {
  13. this.items.push(text);
  14. },
  15. // 数据变更的触发的事件
  16. emitChange: function () {
  17. this.emit('change');
  18. },
  19. // 注册数据 change 监听事件
  20. addChangeListener: function(callback) {
  21. this.on('change', callback);
  22. },
  23. // 移除数据 change 监听事件
  24. removeChangeListener: function(callback) {
  25. this.removeListener('change', callback);
  26. }
  27. });

这是一个简单的 Store 的代码示例,ListStore 继承了 EventEmitter.prototype,就能使用 ListStore.on() 和 ListStore.emit() 来监听和触发事件了。

Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明数据状态已经改变。 View 监听到这个事件,就可以查询到新的数据状态,然后利用最新的数据更新页面。

Controller-View

在 Flux 应用中 Controller-View 不是必须的。但是有了它应用会变得更容易管理。一般来说,Controller-View 会处在整个应用的最顶层,当然里面不会涉及到具体的业务逻辑,主要实现了 Store 和 React 组件之间的绑定,并定义了数据的更新以及传递方式。

Controller-View 会调用 Store 暴露的 getter 获取存储在其中的数据并设置为自己的 state,在调用 render 是会以 props 的形式传递给自己的子组件。

当 Store 响应某个 Action 并更新数据后,会触发一个更新事件,这个更新事件就是在 Controller-View 中进行监听的。当 Store 更新后,Controller-View 会重新获取 Store 中的数据,然后调用 setState 方法触发页面更新,这样所有的子组件就能获取更新后 Store 中的数据了。

View

其实在绝大多数的应用中,View 都是由 React 来实现的,事实上 Flux 并没有限定 View 的具体实现的方式。因此 Angular、Vue 也是不错的选择。

之前的内容也一直在强调:由于 Flux 架构模式的约定,不能直接修改 Store 里面的数据状态,必须由 Dispatcher 分发一个 Action,以保证应用中数据的单向流动性。

ActionCreator

在第一张 Flux 流程模式图中,我们并没有看到 ActionCreator,其实 ActionCreator 也并不是 Flux 的核心概念,但是在许多关于 Flux 的例子和文章中,我们都可以看到这个名词,因此有必要拿出来单独讲解一下。ActionCreator,顾名思义,就是用来创造 Action 的。

在一个应用中,肯定会分发很多的 Action ,这样一个分发 Action 的代码示例如下:

  1. appDispatcher.dispatch({
  2. type: 'ADD_NEW_ITEM',
  3. payload: {
  4. text: text
  5. }
  6. })

分发的 Action 多了,项目中的样板代码就很多,导致代码非常的冗余,这是我们每个开发者都不想看到的。为了减少代码的冗余,同时方便逻辑的重用,我们可以创建 ActionCreator。

  1. // 定义 ActionCreator
  2. function addNewItem(text) {
  3. appDispatcher.dispatch({
  4. type: 'ADD_NEW_ITEM',
  5. payload: {
  6. text: text
  7. }
  8. });
  9. }
  10. // 使用 ActionCreator
  11. handleClick(text) {
  12. addNewItem(text)
  13. }

封装了 dispatch 分发逻辑后,在 View 层,调用分发 Action 的逻辑就异常简洁了,同时当我们后续修改分发的逻辑时,只需修改 ActionCreator 里面的封装逻辑就可以了,而不必修改 View 里面的逻辑。

总结

Flux 架构模式实质上只有 Store、Action、Dispatcher 和 View 四个概念,其实是相当的简单的,但是理解起来还是有一定的难度。

Store 主要用来存储应用的数据,定义数据修改的逻辑,Action 是一个普通的 JavaScript 对象,用来描述 Store 里面数据的变化,而 Dispatcher 是 Action 和 Store 之间的桥梁,Dispatcher 分发 Action,Store 响应 Action 并根据 Action 来更新数据,数据更新后通知 View 层重新渲染。View 层就是视图层,但是 Flux 没有明确的显示 View 的实现,开发者可以根据自己的喜好使用不同的框架进行实现。