关于前端架构中的状态管理方法论,经常能听到的Flux, Redux 和Vuex,这些方法论乍一看好像都差不多,再一看又有一些区别,这是我刚刚接触这些东西时的一个直观感受。看了很多其他人写的文章,自己又画了点图谢谢品味了一下,我决定还是把它们写出来,既能巩固所得,又可以方便以后时常温习。

记得React刚出来不久时,Flux还是React官方指定,Redux那会儿还没有影,当时认真看了Flux,隔了一年多再回头竟然忘了个干净,有人问到我时的表现像是完全没听说过。这也是我重新看过之后决定整理一下的原因。

参考文章中有阮大神早些的力作,代码片段直接拿来主义了:
Flux 架构入门教程
Redux 入门教程

废话不多少,言归正传,关于Flux, Redux和Vuex三者的关系呢,先用一句话总结:

它们都是基于单向数据流的状态管理方法论。Flux最早提出,作为对传统前端MVC的一种改进(我不认为是颠覆)。Redux深受Flux的启发,又加入了函数式编程的思想,算是Flux的极大增强版本。Vuex可以说是基于Flux并且吸收了Redux的一些特点,但它与Vue是紧密捆绑的。Redux其实除了在React中广泛应用,Angular.js以及Angular2中也完成了它的实现。

Flux 要点

Flux
备注:虚线小红框会在比较Redux和Flux时用到。

  • View:
    1. 确定相应的Store以及监听其变化来更新视图。
    2. 发起Action。
// Get binding store
var ListStore = require('../stores/ListStore');
.....
// Subscribe store change event
componentDidMount: function() {
    ListStore.addChangeListener(this._onChange);
}
...
// Pass action to Dispatcher
createNewItem: function (event) {
    // There's a dispatcher instance in ButtonActions
    ButtonActions.addNewItem('new item');
}
  • Action:

    1. 每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)
  • Dispatcher:

    1. 全局唯一。
    2. 逻辑简单,只用来派发action去相应的store。
    3. 通过 AppDispatcher.register() 来登记各种Action的回调函数。
// dispatcher/AppDispatcher.js 全局唯一
var Dispatcher = require('flux').Dispatcher;
var ListStore = require('../stores/ListStore');
module.exports = new Dispatcher();
.....
//注册action回调函数
AppDispatcher.register(function (action) {
  switch(action.actionType) {
    case 'ADD_NEW_ITEM':
      ListStore.addNewItemHandler(action.text);
      ListStore.emitChange();
      break;
    default:
      // no op
  }
})

// actions/ButtonActions.js。dispatch方法只用来分发action
var AppDispatcher = require('../dispatcher/AppDispatcher');
var ButtonActions = {
  addNewItem: function (text) {
    AppDispatcher.dispatch({
      actionType: 'ADD_NEW_ITEM',
      text: text
    });
  },
};
  • Store:
    1. 存放view中的数据。
    2. 发送change事件,通过view中定义的handler捕捉变化。
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');

var ListStore = assign({}, EventEmitter.prototype, {
  // data in View
  items: [],
  .......
  emitChange: function () {
    this.emit('change');
  },
  addChangeListener: function(callback) {
    this.on('change', callback);
  }
  ....
});

Redux 要点

Redux

What is Redux

  • A single, immutable data store.
  • One-way data flow.
  • An approach to change based on pure functions and a stream of actions.

Redux 设计思想

(1)Web 应用是一个状态机,视图与状态是一一对应的。

(2)所有的状态,保存在一个对象里面。

Redux vs Flus: Redux = Reducer + Flux

1.Redux将Flux中的Dispatcher并入了Store。也可以理解为Redux没Dispatcher。Redux的设想是用户永远不会变动数据,应该在reducer中返回新的对象来作为应用的新状态。

2.Redux增加了Reducer.

注:通过代码对比的直观感受就是Flux中的view需要知道具体对应哪个store。而在Redux中,store成为一个被所有view共享的公共对象,view只需要通过store.dispatch()来发送action,无需关心具体处理函数。

  • View:
    1. 通过全局唯一的store dispatch action 以及获取最新state
      注: 通过对比flux的view 部分可以看出在Redux中,view不会关心具体的处理函数,只专注于收发就好。
      Redux View
  • Store:
    1. 全局唯一
    2. 包含整棵state树: state 和 view 是一一对应的关系
    3. Dispatcher功能已被整合进store:store.dispatch()
    4. state 一旦有变化,store 就会调用通过store.subscribe()注册的回调函数(一般是render)。
    5. Reducer的拆分就先不讲了。
const Counter = ({ value, onIncrement, onDecrement }) => (
  <div>
  <h1>{value}</h1>
  <button onClick={onIncrement}>+</button>
  <button onClick={onDecrement}>-</button>
  </div>
);

const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
};

const store = createStore(reducer);

const render = () => {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => store.dispatch({type: 'INCREMENT'})}
      onDecrement={() => store.dispatch({type: 'DECREMENT'})}
    />,
    document.getElementById('root')
  );
};

render();
store.subscribe(render);
  • Action:
    1. 普通对象,作用与Flux中相同
const action = {
  type: 'ADD_TODO', //必填字段
  payload: 'Learn Redux'
};
  • Reducer:
    1. reducer是current stae 和 action 为参数计算new state的纯函数。
    2. 纯函数无副作用:不能调用系统 I/O 的API,不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果 blabla…
const defaultState = 0;
const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'ADD':
      return state + action.payload;
    default: 
      return state;
  }
};
const state = reducer(1, {
  type: 'ADD',
  payload: 2
});
// Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。
// 但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,请参考下面的写法。
function reducer(state, action) {
  return Object.assign({}, state, { thingToChange });
  // Or
  return { ...state, ...newState };
}
// State 是一个数组
function reducer(state, action) {
  return [...state, newItem];
}

Vuex 要点

这是一张来自Vuex官网的图:
Vuex

下面的图是我重新画的,风格与之前的Redux和Flux的数据流图相同,以便与比较:

Vuex

注;代码片段来自Vuex 官方文档

Redux vs Vuex:

  1. 使用mutation来替换redux中的reducer
  2. Vuex有自动渲染的功能,所以无需要专门监听state。(this.$store.getters.doneTodosCount)
  3. Vuex中的action是一个函数集合对象,用于async/sync commit mutaions. 和Redux或者Flux中的action只是简单对象有本质不同,只是叫了一个相同名字。
    注:个人认为action和mutation合在一起就是Redux中的Reducer

先到这里吧,想到什么新的东西再来补充。以上是我对前端状态管理方法论的个人理解,可能有错误的地方,欢迎指正:)

Logo

前往低代码交流专区

更多推荐