作者:小土豆biubiubiu
前言
上一篇文章 [手把手式教程,适合新手入门Vuex]-Vuex入门实践(上),我们一共实践了Vuex
的这些内容。
state-共享数据
在state
中定义共享属性,在组件中可使用$store.state.属性名
访问共享属性。
mutations-修改共享数据
在mutations
中定义修改共享数据
的方法,在组件中可使用$store.commit('方法名')
同步修改共享属性。
actions-异步修改共享数据
在actions
中定义异步修改共享数据
的方法,在组件中可使用$store.dispatch('方法名')
异步修改共享属性。
getters-计算属性
在getters
中定义共享数据的计算属性
,在组件中可使用$store.getters.计算属性名
访问共享数据的计算属性。
那本篇文章我将带大家完成Vuex
的多模块实践。
同时本篇文章也是Vuex
系列的第二篇,第一篇请 点击此处 阅读。
创建多个store模块
前面的文章中,我们在Vuex
的实例方法Store
上分别创建了state
、mutations
、actions
和getters
这几个配置选项。
可以思考一下,当我们的应用程序愈加复杂时,组件之间需要共享的数据也在持续增加,那么state
、mutations
、actions
和getters
配置项里面的代码会愈加庞大。
因此为解决此问题,Vuex
支持每个模块定义自己的state
、mutations
、actions
和getters
。
多个Module配置
现在我们来实践一下多个module
。
首先我们在E:\MyStudy\test\VueDemo\src\vuex
目录下新建两个文件:moduleA.js
和moduleB.js
。
接着分别编辑这两个文件的state
、mutations
、actions
和getters
配置项。
// E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
const moduleA = {
state:{
counter: 100
},
mutations: {
//递增
increase(state) {
state.counter++
},
//递减
decrement(state) {
state.counter--
}
},
actions: {
increaseAction(context) {
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('increase');
},3000)
},
decrementAction(context){
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('decrement');
},3000)
}
},
getters: {
doubleCounter(state) {
return state.counter*state.counter
}
}
}
export default moduleA
// E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
const moduleB = {
state:{
counter: 5
},
mutations: {
//递增
increase(state) {
state.counter++
},
//递减
decrement(state) {
state.counter--
}
},
actions: {
increaseAction(context) {
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('increase');
},3000)
},
decrementAction(context){
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('decrement');
},3000)
}
},
getters: {
doubleCounter(state){
return state.counter*state.counter
}
}
}
export default moduleB
可以看到每个module
定义自己的state
、mutations
、actions
和getters
和直接写在store.js
中的语法相差无几,只是单个module
无需将这些选项挂载到vuex
实例的Store
方法上。
最后就是需要在store.js
中编写该模块的state
、mutations
、actions
和getters
,并且配置这两个module
。
// E:\MyStudy\test\VueDemo\src\vuex\store.js
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './moduleA'
import moduleB from './moduleB'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
counter: 1000
},
mutations: {
//递增
increase(state) {
state.counter++
},
//递减
decrement(state) {
state.counter--
}
},
actions: {
increaseAction(context) {
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('increase');
},3000)
},
decrementAction(context){
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('decrement');
},3000)
}
},
getters: {
doubleCounter(state) {
return state.counter*state.counter
}
},
modules: {
a: moduleA,
b: moduleB
}
})
store.js
中对多个module
的配置语法也比较简单,即在modules
中以字典的形式模块名:模块引用
写入即可。其余配置state
、mutations
、actions
和getters
的方法和前面的一模一样。
Vuex
多个module
的配置完成,现在我们分别按顺序访问store
模块、a
模块和b
模块的共享数据
、同步/异步修改共享数据
以及计算属性访问
。
多个Module-共享数据访问
多个Module
这种情况下,state
中的共享数据被绑定在模块上(模块内部的state
是局部的,只属于模块本身),因此我们需要使用$store.state.模块名称
去访问不同模块的共享数据。而对于store.js
这个根模块
配置的state
数据,直接使用$store.state
访问即可。
那么总结一下:
访问store根模块counter的逻辑:$store.state.counter
访问a模块counter的逻辑为:$store.state.a.counter
访问b模块的counter的逻辑为:$store.state.b.counter
现在我们分别在App.vue
组件中访问store.js
这个根模块的counter
和a
模块的counter
数据,在Index.vue
组件中访问b
模块的counter
数据。
在那个组件中访问那个模块的
state
无所谓,也可以舍弃Index.vue
,将所有代码都写在App.vue
组件中。我在实践时分开写的目的只是想体现多个组件之间可以共享Vuex
中定义的数据。
<!-- E:\MyStudy\test\VueDemo\src\App.vue -->
<template>
<div id="app">
<img src="./assets/logo.png">
<!-- 获取共享数据 -->
<h1>这里是App组件</h1>
<h3> App组件获取共享数据 </h3>
<h3>访问根模块counter——$store.state.counter : {{ $store.state.counter }} </h3>
<h3>访问a模块counter——$store.state.a.counter : {{ $store.state.a.counter }} </h3>
<hr/>
<Index></Index>
</div>
</template>
<script>
import Index from './components/Index'
export default {
name: 'App',
components: { Index }
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<!-- E:\MyStudy\test\VueDemo\src\components\Index.vue -->
<template>
<div>
<h1>这里是Index.vue组件</h1>
<h3>Index组件获取共享数据 </h3>
<h3>访问b模块counter——$store.state.b.counter :{{ $store.state.b.counter }}</h3>
</div>
</template>
<script>
export default {
name: 'Index'
}
</script>
最后我们启动项目查看一下结果。
可以看到,我们已经成功的访问到了不同模块的counter
数据。
多个Module-同步修改共享数据
修改共享数据
现在,我们需要在App.vue
组件中同步修改store
根模块、a
模块的数据,在Index.vue
组件中同步修改b
模块的数据。
前面我们说了state
绑定在模块上,而mutations
它并没有和模块绑定。那么根据 [手把手式教程,适合新手入门Vuex]-Vuex入门实践(上) 这一篇文章中的总结,得知触发counter
递增的方法为$store.commit(‘increase’)
。
由于store
根模块、a
模块、b
模块中使counter
递增和递减的mutations
方法名都是一模一样
的,那么当我们之间使用$store.commit(‘increase’)
去触发counter
递增会出现什么样的结果呢?我们来试一下。
首先在store
根模块、a
模块、b
模块中的mutations
increase
中添加打印console.log
的打印信息,其余代码沿用前面的保存不变。
// E:\MyStudy\test\VueDemo\src\vuex\store.js
//递增
increase(state) {
console.log("store-increase")
state.counter++
}
// E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
//递增
increase(state) {
console.log("moduleA-increase")
state.counter++
},
// E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
//递增
increase(state) {
console.log("moduleB-increase")
state.counter++
},
接着编写App.vue
和Index.vue
的代码。
<!-- E:\MyStudy\test\VueDemo\src\App.vue -->
<template>
<div id="app">
<img src="./assets/logo.png">
<!-- 获取共享数据 -->
<h1>这里是App组件</h1>
<h3> App组件获取共享数据 </h3>
<h3>访问根模块counter——$store.state.counter : {{ $store.state.counter }} </h3>
<h3>访问a模块counter——$store.state.a.counter : {{ $store.state.a.counter }} </h3>
<h3>同步修改根模块counter:<button @click="$store.commit('increase')">同步修改根模块counter</button></h3>
<h3>同步修改a模块counter:<button @click="$store.commit('increase')">同步修改a模块counter</button></h3>
<hr/>
<Index></Index>
</div>
</template>
<script>
import Index from './components/Index'
export default {
name: 'App',
components: { Index }
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<!-- E:\MyStudy\test\VueDemo\src\components\Index.vue -->
<template>
<div>
<h1>这里是Index.vue组件</h1>
<h3>Index组件获取共享数据 </h3>
<h3>访问b模块counter——$store.state.b.counter :{{ $store.state.b.counter }}</h3>
<h3>同步修改b模块counter:<button @click="$store.commit('increase')">同步修改b模块counter</button></h3>
</div>
</template>
<script>
export default {
name: 'Index'
}
</script>
最后我们来看下效果。
那么现象就是:执行一次$store.commit('increase')
会依次触发store
根模块、a
模块和b
模块mutations
中increase
的执行。所以看到当点击button
时这三个模块的counter
都会加1
。
关于触发
increase
的顺序呢,先是根模块
,然后是根据根模块
中modules
的配置顺序决定的。
这个操作呢只是我自己的一个小尝试,真正多模块的mutations
也不会这样去使用。那么想避免上面问题的出现,我们可以避免多个模块中的mutations
方法的重名。
我们来实践一下,store
模块中mutations
的increase
名称不做修改,只修改a
、b
这两个模块。
// E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
mutations: {
//递增
increaseA(state) {
console.log("moduleA-increase")
state.counter++
}
},
// E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
mutations: {
//递增
increaseB(state) {
console.log("moduleB-increase")
state.counter++
}
},
然后对应的需要修改App.vue
和Index.vue
中触发mutations
的代码(只贴出修改部分代码)。
<!-- E:\MyStudy\test\VueDemo\src\App.vue -->
<h3>同步修改a模块counter:<button @click="$store.commit('increaseA')">同步修改a模块counter</button></h3>
<!-- E:\MyStudy\test\VueDemo\src\components\Index.vue -->
<h3>同步修改b模块counter:<button @click="$store.commit('increaseB')">同步修改b模块counter</button></h3>
现在在看下效果。
可以看到不同的按钮触发了不同的模块的mutations
。
命名空间
那么还有一个问题,当module
特别多时,其实不可避免真的会有重名的mutations
,那么此时Vuex
的命名空间
就需要上场了。
命名空间
也很简单,就是在模块中添加一个配置namespaced:true
。
现在我们来实践一下分别给模块a
和模块b
添加命名空间的配置,同时在把mutations
中递增的方法名称统一改回increase
。
// E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
// 省略部分代码
const moduleA = {
// 模块A命令空间配置
namespaced: true,
state:{
counter: 100
},
mutations: {
//递增
increase(state) {
console.log("moduleA-increase")
state.counter++
},
//递减
decrement(state) {
state.counter--
}
}
}
export default moduleA
// E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
// 省略部分代码
const moduleB = {
// 模块B命令空间配置
namespaced: true,
state:{
counter: 5
},
mutations: {
//递增
increase(state) {
console.log("moduleB-increase")
state.counter++
},
//递减
decrement(state) {
state.counter--
}
}
}
export default moduleB
当有了命令空间以后,触发mutations
的方法也就有了变化: $store.commit('模块名/方法')
。
<!-- E:\MyStudy\test\VueDemo\src\App.vue -->
<h3>同步修改根模块counter:<button @click="$store.commit('increase')">同步修改根模块counter</button></h3>
<h3>同步修改a模块counter:<button @click="$store.commit('a/increase')">同步修改a模块counter</button></h3>
<!-- E:\MyStudy\test\VueDemo\src\components\Index.vue -->
<h3>同步修改b模块counter:<button @click="$store.commit('b/increase')">同步修改b模块counter</button></h3>
看下结果。
可以看到命名空间
的效果和前面修改mutations名称不重复
是同样的效果。
多个Module-异步修改共享数据
异步修改共享数据的逻辑和前面同步修改的相同。
我们在actions
中定义了异步同名的递增和递减方法,执行一次$store.dispatch('increaseAction')
,会依次触发执行store
、a
模块和b
模块的actions
。同样我们可以选择让不同模块的actions
方法名称不重复,也可以使用命名空间去解决。
这里我们只演示命名空间
的方式去触发不同module
的actions
。
// E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
const moduleA = {
// 模块A命令空间配置
namespaced: true,
state:{
counter: 100
},
mutations: {
//递增
increase(state) {
console.log("moduleA-increase")
state.counter++
},
//递减
decrement(state) {
state.counter--
}
},
actions: {
increaseAction(context) {
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('increase');
},3000)
},
decrementAction(context){
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('decrement');
},3000)
}
},
}
export default moduleA
// E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
const moduleB = {
// 模块B命令空间配置
namespaced: true,
state:{
counter: 5
},
mutations: {
//递增
increase(state) {
console.log("moduleB-increase")
state.counter++
},
//递减
decrement(state) {
state.counter--
}
},
actions: {
increaseAction(context) {
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('increase');
},3000)
},
decrementAction(context){
setTimeout(function(){
//action通过提交mutation改变共享数据状态
context.commit('decrement');
},3000)
}
},
}
store.js保持不变
接着是组件中触发actions
的代码。
<!-- E:\MyStudy\test\VueDemo\src\App.vue -->
<h3>异步修改根模块counter:<button @click="$store.dispatch('increaseAction')">异步修改根模块counter</button></h3>
<h3>异步修改a模块counter:<button @click="$store.dispatch('a/increaseAction')">异步修改a模块counter</button></h3>
<!-- E:\MyStudy\test\VueDemo\src\components\Index.vue -->
<h3>异步修改b模块counter:<button @click="$store.dispatch('b/increaseAction')">异步修改b模块counter</button></h3>
现在我们看下结果。
可以看到配置命令空间后,已经成功的触发了不同模块同名的actions
。
多个Module-计算属性访问
最后一部分就是多module
的getters
访问了。
首先这个需要说明的是getters
也没有和模块进行绑定,在我们没有给a
和b
模块添加命名空间namespaced:true
的配置前。因为多个模块的getters
存在重名属性,因此控制台可以看到一个错误信息。
后面我们在moduleA.js
和moduleB.js
中添加了命令空间
的配置后该错误就不会在出现。我自己也测试了一下,同样可以像前面那样,保证getters
中的属性不重名,直接使用$store.getters.属性名
去访问不同模块的getters
。
下面来分别实践一下两种访问getters
的方式。
store.js
中getters
的属性名不做修改,依然是doubleCounter
;将moduleA.js
中getters
的属性名改为doubleCounterA
;将moduleB.js
中getters
的属性名改为doubleCounterB
。
// E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
// 省略部分代码
getters: {
doubleCounterA(state) {
return state.counter*state.counter
}
}
// E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
getters: {
doubleCounterB(state) {
return state.counter*state.counter
}
}
接着就是在App.vue
和Index.vue
中访问store
模块、a
模块和b
模块的计算属性。
<!-- E:\MyStudy\test\VueDemo\src\App.vue -->
<h3>访问根模块getters——$store.getters.doubleCounter : {{ $store.getters.doubleCounter }} </h3>
<h3>访问a模块getters——$store.getters.doubleCounterA : {{ $store.getters.doubleCounterA }} </h3>
<!-- E:\MyStudy\test\VueDemo\src\components\Index.vue -->
<h3>访问b模块getters——$store.getters.doubleCounterB : {{ $store.getters.doubleCounterB }} </h3>
浏览器查看结果。
可以看到已经成功的访问到不同模块的getters
。
那么最后我们在尝试将a
、b
两个模块的getters
属性名称改回doubleCounter
,使用命名空间
的方式去访问。
这里不贴
moduleA.js
和moudleB.js
的代码了,直接修改App.vue
和Index.vue
中访问getters
的代码
<!-- E:\MyStudy\test\VueDemo\src\App.vue -->
<h3>访问根模块getters——$store.getters.doubleCounter : {{ $store.getters.doubleCounter }} </h3>
<h3>访问a模块getters——$store.getters['a/doubleCounter'] : {{ $store.getters['a/doubleCounter'] }} </h3>
<!-- E:\MyStudy\test\VueDemo\src\components\Index.vue -->
<h3>访问b模块getters——$store.getters[''b/doubleCounter'] : {{ $store.getters['b/doubleCounter'] }} </h3>
浏览器查看结果。
可以看到命名空间访问成功。
前面访问
getters
的逻辑代码为$store.getters.doubleCounter
。因此在尝试使用命名空间访问getters
时我的代码为$store.getters.a.doubleCounter
。但是发现这种方法会报错,因此灵机一动把代码换成了$store.getters['a/doubleCounter']
,最后访问成功。
总结
到此本篇文章要总结的内容就完成了。篇幅比较长,但是也很简单。
Vuex
的前一篇文章和本篇文章,都是通过$store
对象对state
、mutations
、actions
和getters
进行访问和触发的,因此下一篇文章将会介绍另外一种比较常用的访问触发方式。
Vuex入门实践系列文章
[手把手式教程,适合新手入门Vuex]-Vuex入门实践
这整个系列的总结已经完结,都是一些关于Vuex
基础的用法,比较适合新手刚开始学习和实践Vuex
。
最后呢,将这个系列的所有文章链接整理到这里,方便大家观看。
[手把手式教程,适合新手入门Vuex]-Vuex入门实践(上)
[手把手式教程,适合新手入门Vuex]-Vuex入门实践(中)
[手把手式教程,适合新手入门Vuex]-Vuex入门实践(下)
写在最后
如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者
文章公众号
首发,关注 不知名宝藏程序媛
第一时间获取最新的文章
笔芯❤️~