vuex的原理及简单实现

167 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

一、什么是vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

二、vuex的核心

每一个 Vuex 应用的核心就是 store(仓库)。store基本上就是一个容器,它包含着你的应用中大部分的状态 (state) 。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

三、vuex的核心概念

我们在面试中,我相信一定遇到过面试官问你,vuex有几个主要的部分,你讲一些这种问题。那么接下来我们看一下这几部分的概念。

1. State

Vuex 使用单一状态树,每个应用将仅仅包含一个 store 实例,存储在 Vuex 中的数据和 Vue 实例中的 data 遵循相同的规则。

Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex))。

2. Getters

可以认为是store的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

3. Mutations

我们在上面提到过想要修改store中的状态,只能通过提交 mutation的方式,并且它会接受 state 作为第一个参数。还有一个重要的规则是mutation 必须是同步函数

4. Actions

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。 Action 通过 store.dispatch 方法触发。

5. Modules

当我们单一状态树对象很大时,store会很臃肿,Module就会允许我们将store进行分割,每个模块拥有自己的 state、mutation、action、getter。

然后我们看一个官网给的图,可以看出它大致的工作流程: image.png

四、简单实现

我们会发现,我们在vue的项目中经常会引入一些其他的库,然后会有这样一行代码vue.use(×××),其实这样做是因为会调用插件的install方法,大家可以看看这里cn.vuejs.org/v2/guide/pl…

  1. 从我们使用Vuex的方式,是采用new Vuex.Store,上面也说过vuex的核心就是一个store,所以我们知道vuex主要包括两部分,一个Store类,一个install方法。
  2. 在install方法里面我们知道,vue开发插件第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象,再使用Vue.mixin对钩子函数进行一个混入,注入组件选项。
  3. 在Store类中我们去实现那几个核心部分。

这里我们先不看Modules的实现的简单实现~

let Vue
class Store {
    constructor (options = {}) {
        this.vm = new Vue ({ // 我们的state是响应式的,我们就采用vue中的data来实现
            data() {
                return {
                    state: options.state
                }
            }
        })

        let getters = options.getters
        this.getters = {}
        // getters里面存放的是函数,但是我们直接调用拿到的是值,所以去循环执行下这个返回把结果返回
        Object.keys(getters).forEach((getterKey) => {
            Object.defineProperty(this.getters, getterKey, {
                get: () => { // 这里需要注意this,我们采用箭头函数的方式
                    return getters[getterKey](this.state)
                }
            })
        })

        let mutations = options.mutations
        this.mutations = {}
        Object.keys(mutations).forEach((mutationKey) => {
            Object.defineProperty(this.mutations, mutationKey, {
                get: () => {
                    // mutations实现和getters差不多,这里需要注意,我们在mutations中声明的函数,第一个参数是state,第二个是payload
                    // 在commit中去执行的函数就是执行我们这里,payload就可以从commit方法中拿到了
                    return (payload) => mutations[mutationKey](this.state, payload)
                }
            })
        })

        let actions = options.actions
        this.actions = {}
        Object.keys(actions).forEach((actionsKey) => {
            Object.defineProperty(this.actions, actionsKey, {
                get: () => {
                    // 和mutations逻辑差不多
                    return (payload) => actions[actionsKey](this, payload)
                }
            })
        })
    }

    get state() { // 这里是简化state的访问
        return this.vm.state
    }

    commit = (mutationName, payload) => {
        this.mutations[mutationName](payload)
    }

    dispatch = (actionsName, payload) => {
        this.actions[actionsName](payload)

    }
}

const install = (_Vue) => {
    Vue = _Vue // 构造函数复制给全局变量Vue

    // 在每个页面我们都可以拿到vuex,所以我们在生命周期里做一个混入
    Vue.mixin({
        beforeCreate () {
            // console.log(this) // 这里的this是Vue的实例
            if (this.$options && this.$options.store) { // 这里相当于我们在vue的根实例中引入的store
                this.$store = this.$options.store
            } else {
                this.$store = this.$parent && this.$parent.$store // 在一般的组件中我们去找他父亲的store
            }
        }
    })

}

export default {
    Store,
    install
}

到这里,初步简易的vuex就完成了~