React 数据流与Flux框架

 2016年03月08日    297     声明


React 本身只是一个视图(View),也就是MVC中的V。你可以很容易将React集成到你当前使用MVC框架中,也可以使用FactBook官方推出的Flux框架。Flux框架为React 提供一套单身的数据流(Data Flow)的模式,Flux为我们提供了组织代码和安排内部逻辑的方式,这样我们只需要写很少的代码就能实现想要的功能,使应用更加易于开发和维护。


  1. Flux框架
  2. View
  3. Action
  4. Dispatcher
  5. Store

1. Flux框架

1.1 Flux框架组成

一个基于Flux框架的应用由四部分构成:Action(动作)、Dispatcher(分发器)、Store(数据存储)、View(视图)。各部分作用如下:

  • Action:视图层发出的动作(如:鼠标点击等),提供给Dispatcher并传递数据给Store
  • Dispatcher:处理动作分发,接收Action,并执行相应的回调函数
  • Store:数据交互和逻辑部分,用于存放应用的状态,状态改变后会提醒Views要更新页面
  • View:即,React 组件,用于响应用户交互并

项层的React 组件类似于一个控制视图(Controller-Views),控制视图的组件会与Store进行交互,并传递数据给子组件。在Flux模式中,每个部分都是独立的,通过功能隔离保证了功能简单的性,使每个部分更易于测试和维护。


1.2 Flux数据流

Flux使用单向的数据流,没有双向绑定,使它很容易审查问题来源。其状态是在Store中维护的,所以状态非常易于追踪。Flux数据流可以简单表示如下:

Flux 数据流

很多时候View会通过用户交互触发Action,所以一个完整的数据流类似如下:

Flux 数据流

在上图所示的Flux流程中:

  • 首选需要要一个Action,用户在访问View时通过用户交互(如:'onClick'事件)会触发一个Action
  • Dispatcher收到用户的Action,会将动作分发给Store,即:调用所有注册到Action上的Store回调函数
  • Store根据接收的Action更新自身数据并触发一个'change'事件通知View
  • View收到'change'事件后,根据收到的数据调用setState更新组件

在上述过程中,Flux的每个部分前一步获取输入内容并进行处理,然后向下一步发送输出内容,也就构成了单向数据流循环。这也保证了应用中的各部分分工明确、流程清晰,实现了功能的解耦。


2. View

View是React 组件,它会从Store获取状态(数据),在View中需要绑定'change'事件处理器。

一个View可能会关联到多个Store来管理不同部分的状态,这是因为在React 中更新视图非常简单只需要setStateFlux中复杂的业务逻辑都被封装到Store里面,这样View就只需要响应状态并更新UI即可(MainSection.js)。

var React = require('react');
var MainSection = require('./MainSection');
var Footer = require('./Footer');
var FluxStore = require('../stores/FluxStore');

/**
 * 从FluxStore中获取当关的状态
 */
function getFluxState() {
  return {
    results: FluxStore.getResults(),
    keyword: FluxStore.getKeyword()
  };
}

var FluxApp = React.createClass({

  getInitialState: function() {
    return getFluxState();
  },

  componentDidMount: function() {
    FluxStore.addChangeListener(this._onChange);
  },

  componentWillUnmount: function() {
    FluxStore.removeChangeListener(this._onChange);
  },

  /**
   * @return {object}
   */
  render: function() {
    return (
      <div>
        <MainSection
          results={this.state.results} 
          keyword={this.state.keyword}
        />
        <Footer />
      </div>
    );
  },

  /**
   * FluxStore的'change'事件处理
   */
  _onChange: function() {
    this.setState(getFluxState());
  }

});


3. Action

Flux流程的开始首先要创建Action(动作),Action通过一些action creator方法来定义,这些方法会暴露给外部调用,并通过Dispatcher分发相应的动作。 参见(FluxAction.js):

var AppDispatcher = require('../dispatcher/AppDispatcher');
var FluxConstants = require('../constants/FluxConstants');

var FluxAction = {

  /**
   * @param  {string} text
   */
  search: function(text) {
    AppDispatcher.dispatch({
      actionType: FluxConstants.FLXU_SEARCH,
      text: text
    });
  }

};

Action是用来封装传递数据的,动作只是一个简单的对象,它包含两部分:payload(数据)和 type(类型)。type是一个字符串常量,用来标识动作。


4. Dispatcher

Dispatcher是一个分发中心,它会管理所有数据的流向,并分发ActionStore。一个应用只需要一个Dispatcher分发中心,Dispatcher没有太多其他的业务逻辑(AppDispatcher.js)。

var Dispatcher = require('flux').Dispatcher;

module.exports = new Dispatcher();


5. Store

Store包含应用的状态和业务逻辑,不同的Store管理应用中不同部分的状态。

Store注册给Dispatcher 的回调函数中会接收到分发的Action。回调函数会判断Action中的type并调用具体内部的方法处理来处理Action带来 它会更新 所有注册过回调 都会分发给所有注册的回调,所以回调函数里面要判断这个 action 的 type 并调用相关的内部方法处理更新 action 带过来的数据(payload),最后通知View数据的变更(FluxStore.js)。

var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var FluxConstants = require('../constants/FluxConstants');
var assign = require('object-assign');

var CHANGE_EVENT = 'change';

var _results = {};
var _keyword = '';

// 模似一个搜索源
const _source = [
  {
    name:'IT笔录',
    domain: 'itbilu.com'
  },
  {
    name:'一介布衣',
    domain: 'yijibuyi.com'
  },
  {
    name:'老聂的小站',
    domain: 'niefengjun.cn'
  }
];

/**
 * 实现搜索功能
 * @param  {string} 搜索的内容
 */
function search(text) {
  _keyword = text;
  _source.forEach(function(item){
    if(item.name.indexOf(text)>-1){
      var id = (+new Date() + Math.floor(Math.random() * 999999)).toString(36);
      _results[id] = {
        id: id,
        name:item.name,
        domain: item.domain
      };
    }
  });
}

var FluxStore = assign({}, EventEmitter.prototype, {
  /**
   * 获取Flux 搜索的结果集
   * @return {object}
   */
  getResults: function() {
    return _results;
  },

  /**
   * 获取Flux 搜索的关键字
   * @return {string}
   */
  getKeyword: function() {
    return _keyword;
  },

  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  /**
   * @param {function} callback
   */
  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  /**
   * @param {function} callback
   */
  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }
});

// 注册回调处理函数
AppDispatcher.register(function(action) {
  var text;

  switch(action.actionType) {
    case FluxConstants.FLXU_SEARCH:
      text = action.text.trim();
      if (text !== '') {
        search(text);
        FluxStore.emitChange();
      }
      break;

    default:
      // no op
  }
});