一、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或者手动刷新视图










网友评论