状态管理库
状态管理库根据业务适用场景来分主要分为两类:
- 一类是主要为了解决管理多个组件间共享状态时的组件通信,采用集中式管理应用组件中的状态。目前主流的状态管理框架有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))
)
}
有限状态机
- 状态模式:状态模式一般用来实现有限状态机,应用在游戏或者工作流引擎中。
- 有限状态机事件触发状态的变更和动作的执行,三要素:
- 状态是有限的的;
- 同一时间,只处于一种状态之中
- 状态的扭转是一些行为触发引起的; 原状态 + 对应行为操作 => 新状态;
状态机的实现方式
控制状态间的变化,目前状态机的实现方式有
- 分支逻辑法: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'
}
}
});