手撕Vuex-模块化共享数据上

发布时间 2023-11-05 01:20:31作者: BNTang

前言

好,经过上一篇的介绍,实现了 Vuex 当中的 actions 方法,接下来我们来实现 Vuex 当中的模块化共享数据(modules)。

modules 方法用于模块化共享数据,那么什么叫模块化共享数据呢?其实非常简单。

过去我们将所有模块的数据都放到 state 中共享,例如:

我们有三个模块 首页 / 个人中心 / 登录,那么我们就会将这三个模块的数据都放到 state 中,但是这样会导致命名匮乏的问题。

比如说首页中需要共享name, 个人中心中也需要共享name / 登录中也需要共享name,并且这三个name的取值还不一样, 那么为了能把这三条数据放到同一个state中, 我们就必须指定不同的名称, 例如:

state:{
    homeName: 'www',
    accountName: 'BNTang',
    loginName: 'top'
}

这样就会导致命名匮乏的问题,而且如果我们的项目足够大,那么我们的 state 中就会有很多的数据,这样就会导致我们的 state 中的数据非常多,而且不好管理。

为了解决这个问题,Vuex 就推出了模块化共享数据的方法,那么什么叫模块化共享数据呢?模块化共享数据就是将不同模块的数据放到不同的模块(state)中,这个就是模块化共享数据。

那么模块化共享数据怎么做呢,我们先不要管怎么做,我们先来看看模块化共享数据的好处。

我们还是拿上面的例子来说,我们有三个模块 首页 / 个人中心 / 登录,那么我们就会将这三个模块的数据都放到 state 中,但是这样会导致命名匮乏的问题。

好,到这里我们已经知道了模块化共享数据的好处,我们先来用一下模块化共享数据。

我先不管三七二十一,我在 Store 对象的 store 中定义了一个全局的数据 globalName 取值为 BNTang,

定义完毕之后呢,这个 globalName 代表着全局的数据,那么我们就可以在任何一个模块当中使用这个数据,那么接下来怎么办,例如这个时候我有两个模块分别是首页与个人中心,这两个模块中分别有一个 name 数据,这两个 name 该如何做呢,好我先写代码再来解释。

let home = {
    state: {
        name: '首页'
    },
    getters: {},
    mutations: {},
    actions: {}
}

如上的代码和 Store 中有一套相同的结构,这个对象中保存着首页的 name,保存完毕了之后,还没添加到 Store 中,那么如何添加到 Store 中呢,其实很简单,我们只需要在 Store 中的 modules 中添加这个模块就可以了。

modules: {
    home: home
}

如上代码的写法就代表着,我们 Store 除了保存了全局的共享数据以外还保存了首页模块的共享数据,那么我们的个人中心数据该如何做呢,其实也是一样的(也是同一个世界,同一个梦想的),我们来写一个个人中心的模块。

let account = {
    state: {
        name: '账户'
    },
    getters: {},
    mutations: {},
    actions: {}
}

然后将这个模块添加到 Store 中,方式和上面一样。

modules: {
    home: home,
    account: account
}

好了,到这里我们已经定义了全局共享的数据,与各个模块的共享数据,我们保存归保存,但是我们怎么使用呢,我们来看看。

拿全局共享的数据(随机找一个组件展示数据即可):

<p>{{ this.$store.state.globalName }}</p>

拿首页的共享数据(随机找一个组件展示数据即可):

<p>{{ this.$store.state.home.name }}</p>

拿个人中心的共享数据(随机找一个组件展示数据即可):

<p>{{ this.$store.state.account.name }}</p>

测试效果我不贴图了本人亲自测试过,有个 注意点,我们的 Nuex 还没有实现模块化,所以在测试的时候记得将自己实现的 Nuex 注释掉,打开官方的 Vuex。

到这里,我们的页面就已经展示了全局共享数据与模块化共享数据,完成了将不同模块的数据放到不同的模块(state)中,这个就是模块化共享数据。

模块化中 getters/mutations/actions 的使用

我们知道在 Store 当中,我们可以使用 getters/mutations/actions 这三个方法,那么这三个方法在模块化当中是怎么使用的呢,先看全局的,在全局 Store 分别在这三个方法中定义方法,如下:

getters: {
    getGlobalName(state) {
        return state.globalName + '111111';
    }
},
mutations: {
    changeGlobalName(state, payload) {
        state.globalName += payload;
    }
},
actions: {
    asyncChangeGlobalName({commit}, payload) {
        setTimeout(() => {
            commit('changeGlobalName', payload);
        }, 1000);
    }
},

内容非常的简单,如果是从之前的文章一步一步跟着来的再看如上的代码基本上没有什么压力(我这里不做过多的解释,因为前面讲解的很细,不懂的可以回去翻看之前章节的介绍),然后我们在组件当中使用这三个方法,如下:

<template>
  <div class="hello">
    <p>{{ this.$store.state.globalName }}</p>
    <p>{{ this.$store.getters.getGlobalName }}</p>
    <button @click="globalFn1">同步操作</button>
    <button @click="globalFn2">异步操作</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  methods: {
    globalFn1() {
      this.$store.commit('changeGlobalName', 10);
    },
    globalFn2() {
      this.$store.dispatch('asyncChangeGlobalName', 5);
    }
  }
}
</script>
<style scoped>
</style>

关于测试自行测试,这里不做过多的解释(运行项目,点击页面上面的按钮即可查询效果)。

好了这里我们已经知道了全局的 getters/mutations/actions 的使用,那么模块化当中的 getters/mutations/actions 的使用又是怎么样的呢,我们来看看。

我先不管三七二十一,我在 home 模块中的 getters/mutations/actions 中定义方法,如下:

首页模块:

getters: {
    getHomeName(state) {
        return state.name + '222222';
    }
},
mutations: {
    changeHomeName(state, payload) {
        state.name += payload;
    }
},
actions: {
    asyncChangeHomeName({commit}, payload) {
        setTimeout(() => {
            commit('changeHomeName', payload);
        }, 1000);
    }
}

我这个时候想,我在全局 Store 的 getters/mutations/actions 方法中定义了方法,可以和之前的方式一样使用,那么在模块中的 getters/mutations/actions 方法中定义了方法,我能不能和之前的方式一样使用呢,我们来看看。

首先展示首页模块中的 name, 如果这里直接和之前一样的写法肯定是不能展示的,所以这里我就要提到一个注意点:如果获取的是模块中state共享的数据, 那么需要加上模块的名称

知道了这个注意点之后,我们的代码就可以改写为如下的方式来展示模块中 store 存储的数据,代码如下:

<p>{{ this.$store.state.home.name }}</p>

上面这一点是我们在使用模块化共享数据的时候需要注意的,与寻常的使用方式不同,需要加上模块的名称。

那么我们从 getters/mutations/actions 中获取数据呢,如果获取的是模块中getters共享的数据, 那么不需要加上模块的名称,代码如下:

<p>{{ this.$store.getters.getHomeName }}</p>

mutation 与 action 与使用全局的方式一样,不需要加上模块的名称, 在页面编写两个按钮,一个是同步,一个是异步,在分别实现下各个按钮的点击事件即可, 代码如下:

添加按钮:

<button @click="homeFn1">同步操作</button>
<button @click="homeFn2">异步操作</button>

实现方法:

homeFn1() {
  this.$store.commit('changeHomeName', 10);
},
homeFn2() {
  this.$store.dispatch('asyncChangeHomeName', 5);
}

好了,我们首页模块的 getters/mutations/actions 的使用代码编写好了,我们赶紧趁热来测试一下,看看我们的代码是否能够正常的运行,打开浏览器点击按钮即可完成测试我这里略过。

首页模块的我们编写完毕了,接着来编写个人中心模块的 getters/mutations/actions 的使用,其实和首页模块的 getters/mutations/actions 的使用是一样的,我们来看看,为了节约时间,我直接贴代码:

个人中心模块:

let account = {
    state: {
        name: '账户'
    },
    getters: {
        getAccountName(state) {
            return state.name + '333333';
        }
    },
    mutations: {
        changeAccountName(state, payload) {
            state.name += payload;
        }
    },
    actions: {
        asyncChangeAccountName({commit}, payload) {
            setTimeout(() => {
                commit('changeAccountName', payload);
            }, 1000);
        }
    }
}

页面展示:

<p>{{ this.$store.state.account.name }}</p>
<p>{{ this.$store.getters.getAccountName }}</p>
<button @click="accountFn1">同步操作</button>
<button @click="accountFn2">异步操作</button>

实现方法:

accountFn1() {
  this.$store.commit('changeAccountName', 10);
},
accountFn2() {
  this.$store.dispatch('asyncChangeAccountName', 5);
}

好了,到这里我们的模块化当中的 getters/mutations/actions 的使用就已经完成了。

其实除了在全局的 Store 中添加子模块,子模块还可以添加模块,可以无限的这样添加模块,例如我现在有一个登陆模块,我将登陆模块添加到 account 模块中,登陆模块的代码如下:

let login = {
    state: {
        name: '登录'
    },
    getters: {
        getLoginName(state) {
            return state.name + '333333';
        }
    },
    mutations: {
        changeLoginName(state, payload) {
            state.name += payload;
        }
    },
    actions: {
        asyncChangeLoginName({commit}, payload) {
            setTimeout(() => {
                commit('changeLoginName', payload);
            }, 1000);
        }
    }
}

然后将登陆模块添加到 account 模块中,代码如下:

modules: {
    login: login
}

然后我们在页面中展示登陆模块的数据,代码如下:

<p>{{ this.$store.state.account.login.name }}</p>
<p>{{ this.$store.getters.getLoginName }}</p>

展示数据的注意点与之前一样,需要加上模块的名称,而 getters 就不需要加上模块的名称,然后我们在页面中调用登陆模块的 mutations/actions 的方法,代码如下:

首先是触发按钮:

<button @click="loginFn1">同步操作</button>
<button @click="loginFn2">异步操作</button>

然后是实现方法:

loginFn1() {
  this.$store.commit('changeLoginName', 10);
},
loginFn2() {
  this.$store.dispatch('asyncChangeLoginName', 5);
}

好了,到这里我们的模块化的使用就已经完成了,我们赶紧来测试一下,看看我们的代码是否能够正常的运行,打开浏览器点击按钮即可完成测试我这里略过。