状态管理框架分析

217 阅读3分钟

状态管理库

状态管理库根据业务适用场景来分主要分为两类:

  • 一类是主要为了解决管理多个组件间共享状态时的组件通信,采用集中式管理应用组件中的状态。目前主流的状态管理框架有Redux,Vuex,Mobx等。
  • 一类是基于状态模式,实现的有限状态机类库。主要是管理有限个状态之间的扭转和动作等行为,比如:原状态 + 对应行为操作 => 新状态,更关注在状态扭转层面。目前主流的框架有JavaScript-State-Machine、Xstate,

状态共享,组件通信库Vuex, Redux

Vuex

将组件中的共享状态抽取出来,以一个全局单例的模式来进行管理,vuex的思想借鉴了redux, flux等,是专门为vue设计的状态管理库,vuex内部基于vue组件的响应式机制来进行高效状态的状态更新。

基本使用

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

// 使用
store.commit('increment')
console.log(store.state.count) // -> 1

基本原理 —— vue插件化注入

当使用vue.use(vuex),vuex提供一个install方法供vue来进行调用和执行;当vue版本大于2,采用mixin,将vuexInit方法混入beforeCreate生命周期钩子;当vue版本小于时,将vuexinit合并到init中;

function vuexInit () {
    const options = this.$options
    // store injection
    if (options.store) {
        this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    // 子组件可以访问到store实例;
    } else if (options.parent && options.parent.$store) {
        this.$store = options.parent.$store
    }
}

function applyMixin (Vue) {
  const version = Number(Vue.version.split('.')[0])
  // 当vue版本大于2,采用mixin,将vuexInit方法混入beforeCreate生命周期钩子
  if (version >= 2) {
    Vue.mixin({ beforeCreate: vuexInit })
  // 当vue版本小于时,将vuexinit合并到init中
  } else {
    // override init and inject vuex init procedure
    // for 1.x backwards compatibility.
    const _init = Vue.prototype._init
    Vue.prototype._init = function (options = {}) {
      options.init = options.init
        ? [vuexInit].concat(options.init)
        : vuexInit
      _init.call(this, options)
    }
  }
}

export function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if (__DEV__) {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

基本原理 —— vuex内部的响应式机制

内部实例化了一个vue组件,将state和getters赋值到_vm.data上面,如果_vm.data发现改变,基于vue的响应式更新依赖组件

  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })

Redux

定义好纯函数reducer,来维护需要修改state的行为,修改数据就dispatch进行修改,内部采用观察者模式,来notify需要update的行为

基本使用

function counter(state, action) {
    if (typeof state === 'undefined') {
        return 0
    }
    switch (action.type) {
        case 'INCREMENT':
        return state + 1
        case 'DECREMENT':
        return state - 1
        default:
        return state
    }
}
var store = Redux.createStore(counter)
store.dispatch({ type: 'DECREMENT' })
store.subscribe(render)

基本原理

封装了createtore,接收到reducer,返回store对象;store中包含获取数据的getState,订阅state修改后的函数subcribe,修改state的dispatch;

function createtore(reducer) {
    let currentState;
    let currentListeners = [];
    let currentReducer = reducer();
    const getState = () => {
        return currentState
    }
    const subscribe = ()=> {
        currentListeners.push(listener)
    }
    const dispatch = (action) => {
      currentState = currentReducer(currentState, action)
        const listeners = currentListeners
        for (let i = 0; i < listeners.length; i++) {
            const listener = listeners[i]
            listener()
        }
    }
    const store = {
        dispatch,
        subscribe,
        getState,
    }
    return store
}

观察者模式

redux中利用观察者模式来进行:修改状态后的,同步状态修改行为。观察者(observer)定义统一的update方法,主题(subject),定义 notify方法,并维护观察者列表,当有更新,统一调用观察者的update方法;

class Subject {
	constructor() {
		this.observerList = []
	}

	notify() {
		this.observerList.forEach(item => item.update())
	}

	addObserver(cb) {
		 this.observerList.push(cb)
	}
}


class Observer {
	constructor(cb) {
		if(typeof cb === 'function') {
			this.cb = cb;
		} else {
			throw new Error('错误类型')
		}
	}

	update() {
		this.cb()
	}
}

const observerCallBack = function() {
	console.log('我被通知了')
}

const observer = new Observer(observerCallBack);
const subject = new Subject();
subject.addObserver(observer);
subject.notify()

中间件

redux中间件提供用户一些自定义行为,在dispatch中,日志处理,异常处理等; 本质是将dispatch重写,实现链式调用返回

function applyMiddleware(middlewares) {
    const store = createStore(reducer, preloadedState)
    const middlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

    return {
        ...store,
        dispatch
    }
}

export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return <T>(arg: T) => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args))
  )
}

有限状态机

  • 状态模式:状态模式一般用来实现有限状态机,应用在游戏或者工作流引擎中。
  • 有限状态机事件触发状态的变更和动作的执行,三要素:
    1. 状态是有限的的;
    2. 同一时间,只处于一种状态之中
    3. 状态的扭转是一些行为触发引起的; 原状态 + 对应行为操作 => 新状态;

状态机的实现方式

控制状态间的变化,目前状态机的实现方式有

  • 分支逻辑法:if else/ switch case等对不同行为下状态的处理。适用于状态简单;
  • 查表法:利用map等缓存不同行为下的不同状态。适用于状态多,但是相关的副作用较少;
  • 状态模式:将状态抽象成不同的状态类,有集中的上下文来控制不同状态的变更。适用于状态副作用比较繁琐;

基于Xstate来实现promise之间状态扭转

import { createMachine } from 'xstate';

const promiseMachine = createMachine({
  id: 'promise',
  initial: 'pending',
  states: {
    pending: {
      on: {
        RESOLVE: { target: 'resolved' },
        REJECT: { target: 'rejected' }
      }
    },
    resolved: {
      type: 'final'
    },
    rejected: {
      type: 'final'
    }
  }
});