小胖墩er 小胖墩er
首页
  • 前端文章

    • JavaScript
    • Vue
    • ES6
    • Git
  • Vue
  • React
  • HTML
  • CSS
  • 工具类
  • GitHub技巧
  • 博客搭建
  • 友情链接
💖关于
💻收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

小胖墩er

Better later than never.
首页
  • 前端文章

    • JavaScript
    • Vue
    • ES6
    • Git
  • Vue
  • React
  • HTML
  • CSS
  • 工具类
  • GitHub技巧
  • 博客搭建
  • 友情链接
💖关于
💻收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Vue-Router

  • Vuex

    • Vuex的基本使用
    • 模块化的使用
      • 前言
      • 介绍
      • 命名空间
        • module state
        • module getters
        • module mutation 和 action
      • CodeSandbox
    • commit和dispatch的区别及用法
    • 辅助函数mapMutations解析
  • Vue

  • Vue笔记
  • Vuex
小胖墩er
2021-08-09

模块化的使用

# 前言

上一篇文章我们简单介绍了一下 Vuex 的简单使用,但随之也会产生一个问题。由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。尤其是在多人开发中会显得特别不便利。

# 介绍

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。深入学习可能还是要去看官方文档 (opens new window)

语法:

在Vuex的配置中通过modules配置不同的模块,modules中模块的配置语法是模块名:{state,getters,mutations,action,modules}

import Vue from 'vue'
import Vuex from 'vuex'
import commend from './commend'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    commend: {
        state: {
            name: '小明',
            age: 18
        },
        getters: {
            //some getters
        },
        mutation: {
            // some mutations
        },
        actions: {
            // some action
        },
        modules: {
            // some modules
        }
    } 
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

虽然在我们在实例化vuex.Store时创建了不同的模块,但是最终生成的store对象所有数据(包含子模块的数据)依然存储在同一个state getters mutations actions中。

# 命名空间

vuex中的modules支持命名空间,设置了命名空间的模块与没有设置命名空间的模块存在一定的区别。

语法:在需要开启命名空间的module模块中添加一个配置选项,namespaced:true

# module state

语法:存放在module中的state,是否开启命名空间使用方式都是一样的。都是通过store.state.模块名.state属性名

let store = new Vuex.Store({
  state: {
    name: 'root'
  },
  modules: {
    commend: {
        state: {
            name: '小王' 
        }
    }
    a1: {
      namespaced: true, // 开启命名空间
      state: {
        name: '老李头',
        age: 56
      }
    }
  }
})
// store.state.name           =>  "root"
// store.state.commend.name   =>  "小王"
// store.state.a1.name        => "老李头"
// store.state.a1.age         => 56
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

实例:辅助函数mapState获取指定模块的属性,必须使用 "属性名:函数的形式"

<template>
  <div id="app">
    <button @click="showStore">show</button>
    <p>{{name}}</p>
    <p>{{commendName}}</p>
    <p>{{a1Name}}</p>
  </div>
</template>

<script>
import {mapState} from 'vuex'

export default {
  name: 'App',
  computed: {
    ...mapState(['name']), // $store.state.name  => 'root'
    ...mapState({
      commendName: state => state.commend.name, // $store.state.commend.name  => '小王'
      a1Name: ({a1}) => a1.name // $store.state.a1.name  => '老李头'
    })
  },
  methods: {
    showStore() {
      console.log(this.$store)
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# module getters

语法:所有module中的getter是直接存储在store.getters中的,依然通过store.getters.属性名访问。

所以在开发中如果你的模块如果没有开启命名空间,一定要保证未命名模块与未命名模块之间/未命名模块与根getters之间不能有同名getters属性。

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    // store.getters.test
    test(state) {
      return  state.name + '!!'
    }
     //ERROR! 注意子模块a1中也有一个同名getter,因为a1没有开启命名空间,所以导致两个getter命名冲突产生报错
    reverseName(state) { 
          return state.name.split('').reverse().join('')
     } 
  },

  modules: {
    a1: {
      state: {
        name: '老李头',
        age: 56
      },
      getters: {
        // store.getters.reverseName
        reverseName(state) {  
          return state.name.split('').reverse().join('')
        }
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

注意:当开启了命名空间后module,getters 获取方式发生改变

变为 -->store.getters['模块名/getter属性名']

所以开启命名空间的getter可以与其他模块根store中的getter同名

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    // store.getters.test
    test(state) {
      return  state.name + '!!'
    }
     //正确, a1模块中虽然getters内包好一个与当前根getter同名的属性,但是因为a1模块开启了命名空间.
     // a1的getter最终生成后属性名会发生改变就不会造成命名冲突
    reverseName(state) { 
          return state.name.split('').reverse().join('')
     } 
  },

  modules: {
    a1: {
      namespaced: true,  
      state: {
        name: '老李头',
        age: 56
      },
      getters: {
        // store.getters.reverseName
        reverseName(state) {  
          return state.name.split('').reverse().join('')
        }
      }
    }
  }
})
// store.getters.reverseName   根store的getter => 'toor'
// store.getters['a1/reverseName']  a1模块的getter => "头李老"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

实例:辅助函数mapGetter 获取module的getter方法没有变化的

import { mapGetters} from 'vuex'

export default {
  name: 'App',
  computed: {
    ...mapGetters(['reverseName']),
    ...mapGetters({a1ReverseName: 'a1/reverseName'})
  },
  methods: {
    showStore() {
      console.log(this.$store)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意:模块中getters函数包含四个参数

参数一 state -------- 当前模块的state

参数二 getters -------- 当前模块的getters(如果当前模块没有开启命名空间 该参数的值等于参数四)

参数三 rootState -------- 根state store的state

参数四 rootGetter -------- 根getters store的getters

// 开启命名空间模块getters函数的参数
export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    reverseName(state) {
      return  state.name + '!!'
    }
  },
  modules: {
    a1: {
      namespaced: true, // 开启命名空间
      state: {
        name: '老李头',
        age: 56
      },
      getters: {
        reverseName(state,getters, rootState, rootGetters) {
          console.log('state',state) // {name:"老李头", age: 56}
          console.log('getters',getters) // {reverseName: "头李老"}
          console.log('rootState',rootState) // {name: "root", a1: {name:"老李头", age: 56}}}
          console.log('rootGetters',rootGetters) // {reverseName: "root!!", 'a1/reverseName':"头李老"}
          return state.name.split('').reverse().join('')
        }
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 未开启命名空间模块getters函数的参数
export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    rootReverseName(state) {
      return  state.name + '!!'
    }
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    a1: {
      state: {
        name: '老李头',
        age: 56
      },
      getters: {
        reverseName(state,getters, rootState, rootGetters) {
          console.log('state',state) // {name:"老李头", age: 56}
          console.log('getters',getters) // {rootReverseName: "root!!", 'reverseName':"头李老"}
          console.log('rootState',rootState)  // {name: "root", a1: {name:"老李头", age: 56}}}
          console.log('rootGetters',rootGetters)  // {rootReverseName: "root!!", 'reverseName':"头李老"}
          return state.name.split('').reverse().join('')
        }
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# module mutation 和 action

语法:所有mutation和action也是直接存储在store中的,依然通过store.dispatch('mutation名') store.dispatch('action名')触发的 。

在开发中如果你的模块如果没有开启命名空间,mutation与action 在模块与模块之间 或模块与根store之间存在同名mutation / action 是不会造成命名冲突不会报错的,但是如果commit / dispatch 这些同名 mutation / action时他们将都会执行。

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    rootReverseName(state) {
      return  state.name + '!!'
    }
  },
  mutations: {
    myMutation() {
      console.log('根mutation的 myMutation方法被触发了')
    }
  },
  actions: {
    myAction() {
      console.log('根action的 myAction方法被触发了')
    }
  },
  modules: {
    a1: {
      mutations: {
        myMutation() {
          console.log('模块 a1 mutation 的 myMutation方法被触发了')
        }
      },
      actions: {
        myAction() {
          console.log('模块 a1 action的 myAction方法被触发了')
        }
      }
    }
  }
})

// 触发 $store.commit('myMutation') 时 => '根mutation的 myMutation方法被触发了'  
//                                       '模块 a1 mutation 的 myMutation方法被触发了'

// 触发 $store.dispatch('myAction') 时 => '根action的 myAction方法被触发了'
//                                       '模块 a1 action的 myAction方法被触发了'               
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

注意:当开启了命名空间后module,mutation与action方法名会发生改变

变为 --> '模块名/mutation名' '模块名/action名'

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    rootReverseName(state) {
      return  state.name + '!!'
    }
  },
  mutations: {
    myMutation() {
      console.log('根mutation的 myMutation方法被触发了')
    }
  },
  actions: {
    myAction() {
      console.log('根action的 myAction方法被触发了')
    }
  },
  modules: {
    a1: {
      namespaced: true, // 开启命名空间
      state: {
        name: '老李头',
        age: 56
      },
      mutations: {
        myMutation() {
          console.log('模块 a1 mutation 的 myMutation方法被触发了')
        }
      },
      actions: {
        myAction() {
          console.log('模块 a1 action的 myAction方法被触发了')
        }
      }
    }
  }
})

// $store.commit('myMutation')  => '根mutation的 myMutation方法被触发了'
// $store.commit('a1/myMutation')  => '模块 a1 mutation 的 myMutation方法被触发了'

// $store.dispatch('myAction') => '根action的 myAction方法被触发了'
// $store.dispatch('a1/myAction') => '模块 a1 action的 myAction方法被触发了'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

注意: 模块中mutation函数无论是否开启具名空间依然只包含两个参数

参数一: 当前模块的局部state

参数二: 载荷

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    rootReverseName(state) {
      return  state.name + '!!'
    }
  },
  mutations: {
    myMutation() {
      console.log('根mutation的 myMutation方法被触发了')
    }
  },
  modules: {
    a1: {
      namespaced: true, // 开启命名空间
      state: {
        name: '老李头',
        age: 56
      },
      mutations: {
        myMutation(state, payload) { // state =>  { name: '老李头',age: 56}
          console.log('模块 a1 mutation 的 myMutation方法被触发了')
        }
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

注意: 模块中action函数无论是否开启具名空间依然只包含两个参数

参数一: context 与root state比较多了两个属性

参数二: 载荷

commit  // 用来提交mutation的方法
dispatch // 用来分发action的方法
getters  // 命名空间开启局部getters 没有开启命名空间 全局getters 
rootGetters // 全局getter
rootState // 全局state
state     // 当前模块的statestate
1
2
3
4
5
6

注意:命名空间开启的模块action内部 context.commit 提交mutation时dispatch分发action时会自动添加命名空间前缀,从而实现只提交当前模块mutation/action的效果

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    rootReverseName(state) {
      return state.name + '!!'
    }
  },
  mutations: {
    myMutation() {
      console.log('根mutation的 myMutation方法被触发了')
    }
  },
  actions: {
    myAction() {
      console.log('根action的 myAction方法被触发了')
    }
  },
  modules: {
    a1: {
      namespaced: true, // 开启命名空间
      state: {
        name: '老李头',
        age: 56
      },
      mutations: {
        myMutation() {
          console.log(arguments,'模块 a1 mutation 的 myMutation方法被触发了')
        }
      },
      actions: {
        myAction(context) {
          context.commit('myMutation')  // 会隐式转化为你context.commit('a1/myMutation') 
          context.dispatch('a1/myAction') // 会隐式转化为你context.dispatch('a1/a1/myAction')
          console.log(arguments,'模块 a1 action的 myAction方法被触发了')
        }
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

注意: 如果你想要在开启命名空间模块的action中使用context提交/分发全局mutation/action请在commit dispatch方法传入第三个参数{root:true}代表调用全局

dispatch('someOtherAction', null, { root: true }) 

commit('someMutation', null, { root: true }) 
1
2
3

例子如下

export default new Vuex.Store({
  state: {
    name: 'root'
  },
  getters: {
    rootReverseName(state) {
      return state.name + '!!'
    }
  },
  mutations: {
    myMutation() {
      console.log('根mutation的 myMutation方法被触发了')
    }
  },
  actions: {
    myAction() {
      console.log('根action的 myAction方法被触发了')
    }
  },
  modules: {
    a1: {
      namespaced: true, // 开启命名空间
      state: {
        name: '老李头',
        age: 56
      },
      mutations: {
        myMutation() {
          console.log(arguments,'模块 a1 mutation 的 myMutation方法被触发了')
        }
      },
      actions: {
        myAction(context) {
          context.commit('myMutation',null,{ root: true })  // 提交根mutation 'myMutation'
          context.dispatch('myAction',null,{ root: true }) // 分发根action 'myAction'
          console.log(arguments,'模块 a1 action的 myAction方法被触发了')
        }
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# CodeSandbox

CodeSandbox在线代码演示 (opens new window)

注意 如遇到CodeSandBox打开失败,请尝试按下图操作,然后分窗口就能一边看代码一边看效果啦 sandbox

在线编辑 (opens new window)
#Vuex
上次更新: 2021/11/14, 07:48:46
Vuex的基本使用
commit和dispatch的区别及用法

← Vuex的基本使用 commit和dispatch的区别及用法→

最近更新
01
毛玻璃效果
11-23
02
svg基本绘制
11-23
03
滑动登录界面
11-23
更多文章>
🖥️

© 2021 小胖墩er 💌 粤ICP备2021158933号 🛀 Theme by 💝 Vdoing

  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×