美文网首页
flux源码分析

flux源码分析

作者: zdxhxh | 来源:发表于2019-11-11 08:59 被阅读0次

一、flux介绍

Flux是Facebook用来构建客户端web应用的应用架构。它利用单向数据流的方式来组合react中的视图组件。它更像一个模式而不是一个正式的框架,开发者不需要太多的新代码就可以快速的上手Flux。

flux

可以看到,其中

  • View 视图层
  • Store 数据管理器
  • Dispatcher 事件分发器,用于派发action
  • Action 动作,用于交互或其他View层操作

二、flux的使用流程

image.png

明白了上述几点后,从实际的react应用出发,利用store管理数据

import FluxReduceStore from 'flux/lib/FluxReduceStore'
import Dispatcher from 'flux'
class RouteStore extends FluxReduceStore { 
  reduce(state,payload) { 
    switch(payload.action) { 
      case 'xxx' : { 
        ....
        return state  // 这里必须返回一个state
        break
      }
    }
  }
}
const dispatcher = new Dispatcher()
const RouteStore = new RouteStore(dispatcher)
class RouteView extend React.component { 
  componenentDidMount() { 
    store.addListener(()=> { 
      const newState = store.getState() 
      this.setState({ 
        xxx : newState
      })
    }) 
  }

  click() { 
    const action = { 
      type : 'xxxx',
      payload : 'xxxx'
    }
    dispatcher.dispatch(action)
  }

  render() { 
    return <div>
      <input type="button" onClick={this.click.bind(this)}>
    </div>
  }
}

三、flux源码解析

util

// 判断一个对象是否属于某一个类 @param 1. target<Object> 2. Constructor<Function>
export function _classCallCheck (instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError('Cannot call a class as a function');
  }
}

var validateFormat = function validateFormat (format) { };

if (process.env.NODE_ENV !== 'production') {
  validateFormat = function validateFormat (format) {
    if (format === undefined) {
      throw new Error('invariant requires an error message argument');
    }
  };
}

// 这个方法用于抛出错误,而且是肯定抛出一个错误
export function invariant (condition, format, a, b, c, d, e, f) {
  validateFormat(format);

  if (!condition) {
    var error;
    if (format === undefined) {
      error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
    } else {
      var args = [a, b, c, d, e, f];
      var argIndex = 0;
      error = new Error(format.replace(/%s/g, function () {
        return args[argIndex++];
      }));
      error.name = 'Invariant Violation';
    }

    error.framesToPop = 1; // we don't care about invariant's own frame
    throw error;
  }
}

// 用于开发环境下,如果没有重载抽象方法,则抛出错误
export function abstractMethod (className, methodName) {
  !false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Subclasses of %s must override %s() with their own implementation.', className, methodName) : invariant(false) : undefined;
}

类Dispatcher

import { _classCallCheck, invariant } from './util'

class Dispatcher {
  constructor() {
    _classCallCheck(this, Dispatcher)
    this._callbacks = {}; // 回调map
    this._isDispatching = false;  // 是否在派发action
    this._isHandled = {}; // 完成态
    this._isPending = {}; // 等待态
    this._lastID = 1; // 回调map序号
  }

  // 扔一个回调过去 
  register (callback) {
    const _prefix = 'ID_';
    var id = _prefix + this._lastID++;
    this._callbacks[id] = callback;
    return id;
  }

  // 根据id 删除回调字典某个方法
  unregister (id) {
    !this._callbacks[id] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Dispatcher.unregister(...): `%s` does not map to a registered callback.', id) : invariant(false) : undefined;
    delete this._callbacks[id];
  }

  // 当dispatch(action)时,会按照callback的register顺序依次触发
  // 当如果callback中调用了waitFor方法,则会优先处理具有相应id的callback
  // 但要注意得是 :当两个store互相等待时,会进入死锁状态,即等待的id即处于处理态(pending) 
  // 但也未完成(!isHandled),这个时候会抛出异常(开发环境) 生产环境则直接跳过
  waitFor (ids) {
    !this._isDispatching ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Dispatcher.waitFor(...): Must be invoked while dispatching.') : invariant(false) : undefined;
    for (var ii = 0; ii < ids.length; ii++) {
      var id = ids[ii];
      if (this._isPending[id]) {
        !this._isHandled[id] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Dispatcher.waitFor(...): Circular dependency detected while ' + 'waiting for `%s`.', id) : invariant(false) : undefined;
        continue;
      }
      !this._callbacks[id] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Dispatcher.waitFor(...): `%s` does not map to a registered callback.', id) : invariant(false) : undefined;
      this._invokeCallback(id);
    }
  }
  dispatch (payload) {
    !!this._isDispatching ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.') : invariant(false) : undefined;
    this._startDispatching(payload);
    try {
      for (var id in this._callbacks) {
        if (this._isPending[id]) {
          continue;
        }
        this._invokeCallback(id);
      }
    } finally {
      this._stopDispatching();
    }
  }

  isDispatching () {
    return this._isDispatching;
  }

  _invokeCallback (id) {
    this._isPending[id] = true;
    this._callbacks[id](this._pendingPayload);
    this._isHandled[id] = true;
  }

  _startDispatching (payload) {
    for (var id in this._callbacks) {
      this._isPending[id] = false;
      this._isHandled[id] = false;
    }
    this._pendingPayload = payload;
    this._isDispatching = true;
  }

  _stopDispatching () {
    delete this._pendingPayload;
    this._isDispatching = false;
  }

}

export default Dispatcher

抽象类 FluxStore


import EventEmitter from './EventEmitter'
import { _classCallCheck } from './util'

class YFluxStore {
  constructor(dispatcher) {
    const self = this
    // 检测是否由new 创建的对象
    _classCallCheck(this, YFluxStore);
    // 将FluxStore这个名字缓存在__className
    this.__className = this.constructor.name;
    // 初始化store状态
    this.__change = false
    this.__changeEvent = 'change';
    this.__dispatcher = dispatcher;
    // 创建一个事件车对象
    this.__emitter = new EventEmitter();
    // 往dispatcher对象扔一个回调函数,用于视图派发action回调
    this._dispatchToken = dispatcher.register(function (payload) {
      self.__invokeOnDispatch(payload);
    });
  }
  // 事件车对象发布一个接口
  addListener (callback) {
    return this.__emitter.addListener(this.__changeEvent, callback);
  }

  // 返回自身注册的 派发对象
  getDispatcher () {
    return this.__dispatcher;
  }

  // 这个方法用于dispatcher waitFor 使用
  getDispatchToken () {
    return this._dispatchToken;
  }

  // 返回store最近是否有变化
  hasChanged () {
    // 如果派发对象不在派发状态,则 
    if (!this.__dispatcher.isDispatching()) {
      if (process.env.NODE_ENV !== 'production') {
        // 提示该方法只能在disaptching过程中调用
        invariant(false, '%s.hasChanged(): Must be invoked while dispatching.', this.__className)
      } else {
        invariant(false)
      }
    }
    // 返回是否有变化标识
    return this.__changed;
  }

  // 将store__change标识 赋值为true 
  __emitChange () {
    // 如果派发对象不在派发状态,则 
    if (!this.__dispatcher.isDispatching()) {
      if (process.env.NODE_ENV !== 'production') {
        invariant(false, '%s.__emitChange(): Must be invoked while dispatching.', this.__className)
      } else {
        invariant(false)
      }
    }
    this.__changed = true;
  }

  // 如果dispatcher对象派发了一个action 做相应处理 并执行__onDispatch
  invokeOnDispatch (payload) {
    this.__changed = false;
    this.__onDispatch(payload);
    if (this.__changed) {
      this.__emitter.emit(this.__changeEvent);
    }
  }

  __onDispatch (payload) {
    if (!false) {
      if (process.env.NODE_ENV !== 'production') {
        invariant(false, '%s has not overridden FluxStore.__onDispatch(), which is required', this.__className)
      } else {
        invariant(false)
      }
    }
  };

}

export default YFluxStore

实现类FluxReduceStore

import YFluxStore from './YFluxStore'
import { _classCallCheck, abstractMethod, invariant } from './util'
class YFluxReduceStore extends YFluxStore {
  constructor(...args) {
    super(...args)
    this._state = this.getInitialState();
  }

  getState () {
    return this._state
  }
  getInitialState () {
    return abstractMethod('FluxReduceStore', 'getInitialState');
  }
  reduce (state, action) {
    return abstractMethod('FluxReduceStore', 'reduce');
  }
  areEqual (one, two) {
    return one === two;
  }
  __invokeOnDispatch (action) {
    this.__changed = false;

    // 解决当action要对state进行更新时,必须时更新
    // 缓存旧state
    var startingState = this._state;
    // 缓存新state 调用reducer处理state
    var endingState = this.reduce(startingState, action);

    // This means your ending state should never be undefined.
    // 这意味着reducer必须返回点东西

    !(endingState !== undefined) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s returned undefined from reduce(...), did you forget to return ' + 'state in the default case? (use null if this was intentional)', this.constructor.name) : invariant(false) : undefined;

    // 对比新旧状态是否是同一个对象
    if (!this.areEqual(startingState, endingState)) {
      this._state = endingState;

      // `__emitChange()` sets `this.__changed` to true and then the actual
      // change will be fired from the emitter at the end of the dispatch, this
      // is required in order to support methods like `hasChanged()`
      // 通知state改变了
      this.__emitChange();
    }

    if (this.__changed) {
      this.__emitter.emit(this.__changeEvent);
    }
  };
}

export default YFluxReduceStore

流程如下 :


flux与视图的交互流程

四、本章小结

flux架构可谓是redux,dva等用于spa的数据管理的鼻祖,它使用单向数据流的方式,将复杂的单页应用不同视图进行状态提升,统一管理,在我眼里,这个框架的功能点还是少了一些,但它的思想值得我们去借鉴。

而用过其他框架,发现这个框架的不足之处在于以下几点。

  • 使用繁琐 dispathcer这个调度器其实没必要存在,它可以跟store是一对一关系,也可以是一对多的关系。而在多个store或者树形store,管理dispatcher又是一笔不小的开销
  • store注入视图的方式不太优雅,需要手动添加回调事件对页面进行刷新
  • 状态的跟踪不能做到细粒度的变化,areEqual方法用与比较新旧状态的是否发生改变,而它内部的实现只是进行简单的地址比较(===),这会导致两个问题 : (1) 对于state下的子属性不能进行深层次的跟踪 (2) 每次reduce想要刷新视图必须返回一个新的state或者手动刷新视图

相关文章

网友评论

      本文标题:flux源码分析

      本文链接:https://www.haomeiwen.com/subject/uzvzbctx.html