Vue 状态管理【vue 知识汇点4】

816 阅读3分钟

状态管理就是:把组件之间需要共享的状态抽取出来,遵循特定的约定,统一来管理,让状态的变化可以预测。

之所以做状态管理,就是为了以下好处:

  1. 有效分离 UI 和数据处理
  2. 帮助前端应用结构化数据
  3. 有效控制状态的变化

⚠️注意:这篇文章讲的都是状态管理都是保证单一数据源单向数据流,单一数据源要求关键数据都从一个地方获取,单向数据流要求状态管理的参与者都按照一条流向来获取数据和发出动作。

实现状态管理的几种方式

store 模式

最简单的处理就是把状态存到一个外部变量里面,比如 this.root.root.data,也可以是一个全局变量

// store 里有一堆的 action,这些 action 来控制 state 的改变,也就是不对 state 直接改变
var store = {
    // state 储存数据
	state: {
    	message: 'hello!'
    },
    setMessageAction(newValue) {
    	this.state.message = newValue;
    },
    clearMessageAction() {
    	this.state.message = ''
    }
}

但上面的方式,组件还是可以不通过 action 来修改 state,那我们也没办法跟踪这些修改是怎么发生的。所以我们就需要规定,组件必须通过 action 来改变 state,进化一下,一个 Flux 就出来了

Flux

Flux 其实是一种思想,Flux 把一个应用分为四个部分:View(展示数据)、Action、Dispatcher(中转站,接收所有Action。然后发送给所有的Store)、Store(存数据)

Flux 最大的特点就是数据都是单向流动的,

Redux

Redux 和 Flux 思想类似,但也有差别

  1. 用户通过 View 发出 Action; store.dispatch(action)
  2. Store 自动调用 Reducer,并且传入两个参数:当前 state 和收到的 Action,Reducer 会返回新的 State let nextState = xxxReducer(previousState, action)
  3. State 一旦有变化,Store 就会调用监听函数 store.subscribe(listener)
  4. listener 可以通过 store.getState() 得到当前状态,如果使用的是 React。这是可以重新渲染 View
    function listener() {
    	let newState = store.getState();
        component.setState(newState)
    }
    

对比 Flux:Flux 中的 Store 都是各自为战,每个 Store 只对对应的 view 负责,每次更新都只通知对应的 View。Redux 中各子 Reducer 都是根 Reducer 统一管理的,每个子 Reducer 的变化都要经过根 Reducer 的整合。Flux 和 Redux 都是单向数据流

总的来说,Redux 有三大原则:

  1. 单一数据源,Flux 的数据源可以是多个
  2. state 是只读的(store 收到 action 后,必须给一个新 State),flux 的 state 可以随便改
  3. 使用纯函数来执行修改

中间件

刚才讨论的都是比较理想的同步状态。在实际项目中,一般都会有同步和异步操作,在 Redux 中,同步的表现就是:Action 发出以后,Reducer 立即算出 State,异步的表现是:Action 发出以后,过一段时间再执行 Reducer

Redux 引入了中间件 Middleware ,在 view 里发送 Action 时,加上一些异步操作,过程是:

  • 请求开始时,dispatch 一个请求开始 Action,触发 State 更新为 “正在请求”状态,View 重新渲染,比如展现个 Loading
  • 请求结束后,如果成功,dispatch 一个请求成功 Action,隐藏掉 loading,把新数据更新到 state。如果失败,dispatch 一个请求失败 Action,隐藏掉 loading,给个失败提示

Vuex

  • Store:每个Vuex 都有一个全局的 store,包含着应用中的 state,通过 store 选项把 state 注入到整个应用中,this.$store 可以访问到 state,state改变,view 会跟着改变
  • mutation(同步事务):state 不能直接改,更改 vuex 的store重的state唯一方法就是提交 mutation。通过store.commit('xxx')
  • Action(异步事务):通过store.dispatch('xxx')来触发 action,

实现简易的 Vuex

实现 store 类

class Store {
	constructor(options) {
    	this.state = new Vue({
        	data: options.state,
        })
        this.mutations = options.mutations;
        this.actions = options.actions
        options.getters && this.handleGetters(options.getters)
    }
    commit = (type, arg) => {
    	this.mutations[type](this.state, arg)
    }
    dispatch(type, arg) {
    	this.actions[type](
        	{
            	commit: this.commit,
                state: this.state
            },
            arg
        )
    }
    handleGetters(getters) {
    	this.getters = {}
        Object.keys(getters).forEach(key => {
        	Object.defineProperty(this.getters, key, {
            	get: () => {
                	return getters[key](this.state)
                }
            })
        })
    }
}

因为使用 Vue.use() 插件,必须提供 install 方法

let Vue
function install(_vue) {
	Vue = _vue;
    Vue.mixin({
    	beforeCreate() {
        	if(this.$options.store) {
            	Vue.prototype.$store = this.$options.store
            }
        }
    })
}