前言
对于复杂点的前端应用来说,状态管理是一个避不开的问题。应用的组件间需要共享某些状态,此时如果不对状态进行有效地管理,则会非常混乱,状态的改变会无法跟踪。此时工程师给出的思路就是将应用的状态提取出来,统一管理,严格按照流程来进行修改,让状态变化可预测。这里的祖师爷即为Flux,而Redux和Vuex为Flux架构的继承者,根据自身框架的特点进行改进,下面分别聊聊这三种状态管理架构。
Flux

Flux把应用分为四部分:
- View:视图层,根据Store的数据渲染页面
- Store:负责存储数据,发生变动时通知View进行更新
- Action:关于修改Store数据的行为描述,一般是一个对象
- Dispatcher:接收Action作为参数并将其派发给所有Store
总结下Flux架构下用户交互的一般流程就是:
- View层中用户通过点击或者其他操作触发回调
- 回调中调用Dispatcher来派发Action给所有Store
- 接收到Action的Store根据Action的内容进行更新数据,并派发事件等方式通知对应的View层也进行更新
Flux架构下有以下特点:
- 一个应用下可以有多个Store,多个Store可以分别跟多个View进行绑定。
- Store的变化只能通过Dispatcher分发Action来实现,这保证了数据的单向流动,保证流程的清晰
- 修改State的可以不是纯函数
Redux
与Flux的区别
- 在Flux中,一个应用可能有多个Store,这些Store会分别于多个View进行绑定,这就会让状态的变化可能会变得不够清晰。所以Redux对此进行了改进,一个应用只能拥有一个Store。
- 由于只有一个Store,所以就没有了dispatcher的概念,dispatch方法被集成到了Store当中
- 只能使用纯函数得到新的State,不能修改State。
三大原则
- 单一数据源,整个应用的state存储在一个状态树上面,而这个状态树存储于唯一的一个store中。这使得保存应用状态变得非常简单。
- state只读,除了dispatch action,任何其他方法都无法直接修改state,通过这种集中化处理,可调试性大大提高
只能使用纯函数修改,每次都返回一个新对象,这使得时间旅行之类的功能成为可能。
核心概念
Reducer
一个以接收旧state和action作为参数,返回新state的纯函数。这个函数不能在原有state对象上进行修改,而是要返回一个全新的state,这样View才能更新。
Action
与Flux的Action概念基本一致,是一个描述改变Store数据的对象。
其中Redux还提出ActionCreator的概念,是一个生成Action的函数,避免用户重复生成Action的麻烦。Store
通过
createStore函数接收一个reducer作为参数创建。核心API
createStore(reducer, [initialState], [enhancer])
Redux通过此方法创建Store
reducer,接收state与action作为参数,返回下一个状态树
- initialState,Store的初始状态树
- enhancer,用于加强Store的功能,一般为applyMiddleware
combineReducers(reducers)
用于结合多个Reducer,Redux通过此方法来实现状态的模块化管理,以避免Flux当中存在多个Store的情况。
其中reducers的组织形式是一个key-reducer的对象,创建对应的store对应的state树也可以通过对应的key来得到对应的state,例子如下:const todosReducer = (state = [], action) => {}const counterReducer = (state = 0, action) => {}const reducers = combineReducers({todos: todosReducer,counter: counterReducer})const store = createStore(reducers)console.log(store.getState())// {// todos: ...,// counter: ...// }
applyMiddleware(…middleware)
中间件是Redux提供的位于Action发起之后,Reducer执行前的扩展点。在这个扩展点中常见的应用是打印日志,异步IO等等。
一个经典的middleware的函数结构如下:
具体的演变思路可以看这里,每个中间件的目的就是要对前一个const logger = store => next => action => {console.group(action.type)console.info('dispatching', action)let result = next(action)console.log('next state', store.getState())console.groupEnd(action.type)return result}
dispatch函数进行封装形成一个新的dispatch函数。所以applyMiddleware的大体实现如下: ```javascript function applyMiddleware(…middlewares) { return (createStore) => (reducer, preloadedState) => { const store = createStore(reducer, preloadedState)
getState: store.getState, dispatch: (action, …args) => dispatch(action, …args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(…middlewares)(store.dispatch) return { …store, dispatch } } }const middlewareAPI = {
其中compose函数是函数式编程的一个概念,它的意思是将多个同类型函数(功能一致,参数类型、返回值类型一致)进行组合,形成一个新的函数。具体实现如下:```javascriptfunction compose(...funcs) {if (funcs.length === 0) {return (arg) => arg}if (funcs.length === 1) {return funcs[0]}return funcs.reduce((a, b) => (...args: any) => a(b(...args)))}
总结流程就是先将中间件数组映射为传入了store后的形状为 next => action => {...} 函数数组。接着通过compose将这个函数数组进行组合,此时得到的还是形状为 next => action => {...} 的函数。接着将最初始的 store.dispatch 传入此函数,最终得到需要的 dispatch 函数。
bindActionCreators(actionCreator, dispatch)
ActionCreator即为创建Action的函数,bindActionCreators就是将action跟store.dispatch进行绑定,直接向绑定后的函数传入payload即可自动dispatch相关的action。例子如下:
const CounterAddActionCreator = (count) => {return {type: 'ADD_COUNT',count}}const boundCounterAddAction = bindActionCreator(CounterAddActionCreator, store.dispatch)boundCounterAddAction(10) // 相当于 store.dispatch(CounterAddActionCreator(10))
store.dispatch(action)
store.subscribe(listener)
store.getState()
Vuex
Vue当中的状态管理工具为Vuex,它的核心理念也是单向数据流。
核心概念
State
与Redux类似,Vuex中的State也是单一状态树,一个对象包含应用的所有状态。
Getter
类似于计算属性,每个getter接收state作为参数,返回运算后的值,相关的值会进行缓存,直至依赖值发生变化。
Mutation
提交mutation是Vuex中改变state的唯一方式,在这里处理的只能是同步内容。
通过在配置对象的mutations选项下添加对应方法即可完成mutation的注册,每个mutation方法接收state与payload作为参数,然后对state的内容进行修改。通过 store.commit 方法提交mutation,完成对state的修改。具体例子如下:
const store = new Vuex.Store({state: {count: 1},mutations: {increment (state) {state.count++}}})store.commit('increment')
Action
Action的功能是提交mutation和进行任意具有副作用的操作(包括异步请求、修改localStorage等等)。这点是Vuex比Redux更为强大的地方,Redux原则上不支持任何的副作用操作,要进行这些操作必须要先通过中间件。
Module
Vuex中的模块化方案为Module,每个module都有自己的state, getter, mutation, action甚至module。
