vuex

文章目录

Vuex

vuex初始

vuex 定义 vuex 是一个状态管理的插件,可以解决不同组件之间的数据共享和数据持久化。通俗点的说法就是全局的数据仓库,在组件里面的data是组件里的数据仓库也就是局部的数据仓库,全局数据仓库里面的数据可以在任意组件中共享。
vuex 是一个专门为 vue.js 设计的集中式状态管理架构。状态?我把它理解为在 data 中的属性需要共享给其他 vue 组件使用的部分,就叫做状态。

简单的说它就是全局 data.

流程

State 的数据渲染到 Component 中,Component dispatch(分发) Actions(异步操作,与 BackEnd(后端) API 相关的操作),再由 Actions commit(提交) Mutations(要执行的同步操作),Mutations 再修改 State 的数据,State 的数据再渲染到 Component 中,如此循环。

当然中间如果没有异步操作,可以跳过 dispatch 到 Actions 的操作,直接 commit Mutations 执行同步操作。

当然直接修改 State 的数据也可以实现,但这样修改不会被反应在 Devtools 中,Devtools 不会保存数据修改的快照,所以不要这样。只有是 Mutations 的操作,才会被反应到 Devtools,所以这也是 Mutations 中不要写异步操作的原因(它也不会被记录到 Devtools 中),这样 Devtools 的数据(和快照)就会不准确,和页面显示不一致。

安装vuex

  1. npm install vuex --save
  2. 在 main.js 的同级目录新建store文件夹 里面新建一个 index.js
  3. 在 index.js 里面引入 vuex
1
2
3
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
  1. 向外暴露vuex实例
1
2
3
4
5
6
7
8
const store = new Vuex.Store({ // 注意调用的是 Vue.Store() 方法来创建实例
state:{},
mutations:{},
getters:{},
actions:{},
modules:{},
})
export default store
  • state 一- vuex store 实例的根状态对象,用于定义共享的状态变量,就像 Vue 实例中data
  • mutations 一 可以理解为 store的methods(同步操作 )
  • getters 一 读取器,外部程序通过它获取变量的具体值,或者在取值前做一些计算(可以认为是 store 的计算属性)
  • actions 以理解为 store的methods(异步操作 如ajax)
  • modules 以模块化的方式写vuex (如购物车对应一个module 个人中心对应一个module )
  1. 挂载到vue实例类上
1
2
3
4
5
6
7
import store from './store'

new Vue({
router,
store, // 和 router 一样,同样要这样挂载
render: h => h(App)
}).$mount('#app')

开始

获取state里面的值

  1. 在state里面放入数据
1
2
3
4
5
6
7
8
9
10
11
export default new Vuex.Store({
state: {
num:1
},
mutations: {

},
actions: {

}
})
  1. 在某个组件获取这个数据 用computed来接收这个数据
1
2
3
4
5
computed: {
list() {
return this.$store.state.num
}
}

更改state里面的值 mutations(同步提交)

更改state里面的值 更改 Vuex 的 store 中的状态的 唯一方法 是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
“同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行。

每个 mutation 都建议执行单一功能,比如在购物车中,可能会有 addCart (添加购物车) 的 mutation,它里面可能会有 商品数量 + 1 ,和添加新商品两种功能。我们应该把这两种功能写成两个 mutation,而不是被整合到 addCart 中。而判断数量 + 1还是添加新商品的逻辑,建议放到 action 中,它不止可以处理异步,还可以处理复杂的逻辑。

在mutation里面写入更改的函数 函数的第一个参数代表 state 更改vuex中的state是通过更改这个参数实现的
Mutation 必须是同步函数
为什么必须是同步函数呢 因为在 devtools 日志里面监测不到数据的变化

1
2
3
4
5
6
7
8
9
10
export default new Vuex.Store({
state: {
num:1
},
mutations: {
updateNum(state){
state.num ++ // this.$store.commit("updateNum")
}
}
})

提交载荷(Payload) 其实就是提交的时候附带参数 只能提交一个载荷

1
2
3
4
5
6
7
8
9
10
export default new Vuex.Store({
state: {
num:1
},
mutations: {
updateNum(state,val){
state.num += val; // this.$store.commit("updateNum",2)
}
}
})

提交多个载荷, vuex 不支持提交多个载荷 提交多个载荷需换种写法, 换成对象或数组

1
2
3
4
5
6
7
8
9
10
export default new Vuex.Store({
state: {
num:1
},
mutations: {
updateNum(state,val){
state.num += val.val; // this.$store.commit("updateNum",{val:1,val1:2})
}
}
})

mutations 的另一种提交风格

在此之前的 this.$store.commit('mutationName') 是普通的提交风格,还有另外一种提交风格,这样就是提交 payload(载荷) 了。

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
<!-- 某个组件 -->
<template>
<div>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
</div>
</template>

<script>
export default {
methods: {
addCount(n) { // 当然如果把传入参数这个 n 改为 val,下面就可以改写成 val 了
this.$store.commit({
type: 'increment',
val: n // 可以改写成 val, 冒号不用了,这是 ES6 对象的增强写法
})
}
}
}
</script>

<!-- store/index.js ,为了高亮在外面补了一个 script 标签,实际上没有 -->
<script>
export default new Vuex.Store({
state: {
num:1
},
mutations: {
increment(state, payload){ // 这种提交方式,第二个参数(payload) 就是一个对象而不是一个值了
console.log(payload) // { type: 'increment', val: 5 } // 第二次打印 val 为10
state.num += payload.val;
}
}
})
</script>

action(异步操作,之后再通过 mutation 来更改state)

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。 // action 里面提交数据是提交mutation 在mutation里面是直接修改state
  • Action 可以包含任意异步操作。
  • 它会传入一个 context 上下文对象作为第一个参数(如果是写在 new 出来的 Vue.Store 里,那就是 new 出来的 Vue.Store,如果是写在 module 里,那就指模块的上下文,可以打印一下,相对于根来说,多出来来了 rootState 和 rootGetters 的属性),commit 的方法,只会在当前的上下文中进行寻找,第二个参数可选,是 payload,传入的参数(如果是第二种提交风格就会拿到一个对象,正常提交就会拿到传入的值)。
  • this.$store.dispatch(actionName, payload) 最后将返回传入的 action 函数的结果,如果没有 return ,就是 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 简单用法
export default new Vuex.Store({
state: {
num:1
},
mutations: {
updateNum(state,val){
state.num += val;
}
},
actions: {
asyncUpdate(context ,val){
setTimeout(function(){
context.commit("updateNum",val) //this.$store.dispatch('asyncUpdate',5) 分发 Action
},400)
},
asyncUpdate({state, commit} ,val){ // 解构写法
console.log(state, commit) // 拿到了 Store 的 state 对象 和 commit 方法
setTimeout(function(){
commit("updateNum",val)
},400)
},
}
})

异步回调

完成异步操作后,如果我们想要执行一个回调函数,可以这样写

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
<!-- 某个组件 -->
<template>
<div>
{{$store.state.num}}
<button @click="updateInfo">update</button>
</div>
</template>

<script>
export default {
methods: {
updateInfo() {
// 直接传入一个函数
this.$store.dispatch('asyncUpdateNum', function () {
console.log('Done.')
})
}
}
}
</script>

<!-- store/index.js ,为了高亮在外面补了一个 script 标签,实际上没有 -->
<script>
export default new Vuex.Store({
state: {
num:1
},
mutations: {
updateNum(state,val){
state.num += val;
}
},
actions: {
asyncUpdateNum(context, payload) {
setTimeout(() => {
context.commit('updateNum')
payload() // 直接执行了传入的函数。
}, 1000)
}
}
})
</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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!-- 某个组件 -->
<template>
<div>
{{$store.state.num}}
<button @click="updateInfo">update</button>
</div>
</template>

<script>
export default {
methods: {
updateInfo() {
this.$store.dispatch('asyncUpdateNum', {
message: 'msg', // 当然 message 也可以写成一个对象
success: () => {
console.log('Done.')
}
})
}
}
}
</script>

<!-- store/index.js ,为了高亮在外面补了一个 script 标签,实际上没有 -->
<script>
export default new Vuex.Store({
state: {
num:1
},
mutations: {
updateNum(state,val){
state.num += val;
}
},
actions: {
asyncUpdateNum(context, payload) {
setTimeout(() => {
context.commit('updateNum')
console.log(payload.message)
payload.success() // 执行了回调
}, 1000)
}
}
})
</script>

我们发现,上面这种写法虽然实现了回调函数,但由于携带的信息(message)和回调的函数(success)写到一起去了,不够优雅。所以下面用了 Promise 来分离它们。

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
46
<!-- 优雅地执行完成 action 的回调函数 -->
<!-- 某个组件 -->
<template>
<div>
{{$store.state.num}}
<button @click="updateInfo">update</button>
</div>
</template>

<script>
export default {
methods: {
updateInfo() {
// this.$store.dispatch 返回了一个 Promise,因此用 then 可以执行成功回调,并且完成分离
this.$store
.dispatch('asyncUpdateNum', 'msg')
.then(msg => console.log(msg))
}
}
}
</script>

<!-- store/index.js ,为了高亮在外面补了一个 script 标签,实际上没有 -->
<script>
export default new Vuex.Store({
state: {
num:1
},
mutations: {
updateNum(state,val){
state.num += val;
}
},
actions: {
asyncUpdateNum(context, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateNum')
console.log(payload) // 拿到了携带的信息,在这里是字符串
resolve('Done.') // resolve 修改 Promise 的状态, 并传回成功的信息
}, 1000)
})
}
}
})
</script>

getters (相当于计算属性)

当我们在多个组件里都用到了 State 里的属性,并把它做成了一个 computed 属性,并在页面中使用,但每个组件都写一遍 computed ,非常麻烦,我们可以在全局声明一次 getters 即可在各个组件中达到声明了 computed 一样的效果。它接受两个参数,第一个是 state,第二个是 getters。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default new Vuex.Store({
state: {
num:1
},
getters:{
nums(state){
return state.num + "元" // this.$store.getters.nums
}
},
mutations: {
updateNum(state,val){
state.num += val;
}
}
})

和 computed 一样,它也可以直接返回一个方法,用来接收传入值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default new Vuex.Store({
state: {
num:1
},
getters:{
nums(state){
return state.num + "元" // this.$store.getters.nums
},
newNums(state, getters) {
return function(newWord) { // 这样写之后,就可以在 mustache 里写 {{$store.getters.newNums(',谢谢')}} 最终将会显示 1元,谢谢
return getters.nums + newWord
}
}
}
})

Module vuex模块化

vuex官方解释

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

通俗点的说法

当我们写大型的项目的时候 我们所有的需要共享的信息或状态都写在同一个 state 里面 导致 state 或其他的操作比较臃肿 那么 为了解决这个问题 vuex提出了模块化 modules.

下面是我们模块化的写法,在 main.js 的同级新建一个 store 文件夹 在这个 store 文件夹新建一个 index.js 里面写上一些内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

import moduleA from "./modules/moduleA"
import moduleB from "./modules/moduleB"
export default new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})

在 index.js 的同级新建一个 modules 的文件夹 这个文件夹分别有两个 js 分别为 moduleA 和 moduleB 内容分别为以下内容,再在 main.js 里面引入 index.js ,这个模块化的写法就已经完成 。

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
const moduleA = {   // moduleA
state: { num:1 },
mutations: {
addNum(state){
state.num += 2 // 调用的时候执行 this.$store.commit('addNum')
}
},
actions: {
asyncAddNum(context,val){
context.commit("addNum",val) // 调用的时候执行 this.$store.dispatch('asyncAddNum')
}
},
getters: {
addNums(state){
return state.num + "元 " // 调用的时候执行 this.$store.getters.addNums
}
}
}
export default moduleA

const moduleB = {
state: { num:2 },
mutations: {
addNum(state){
state.num += 3
}
}
}
export default moduleB

注意,没有开启命名空间时,可以直接写 this.$store.commit('addNum')、this.$store.dispatch('asyncAddNum', 1)、this.$store.getters.addNums 这样类似的写法,而不需要像访问模块里的 state 一样,加上 .模块的前缀,如 this.$store.state.a.num ,因为没有开启命名空间时,前面的三种属性的访问都会先去根 Store 中寻找,找不到就会自动继续去子模块中寻找,直到找到为止。

模块化注意点

  1. 对于上面的模块化的写法 只有state是模块化的 其他的诸如 getters mutations actions不是模块化
  2. 如果想实现getters mutations actions实现模块化 那么 得在每个模块的js里面加入 *命名空间 *
    namespaced: true,
  3. 启用命名空间之后 获取getters mutations actions的写法都要变
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
const moduleA = {   // moduleA
namespaced: true,
state: { num:1 },
mutations: {
addNum(state){
state.num += 2 // 调用的时候执行 this.$store.commit('a/addNum',3)
}
},
actions: {
syncAddNum(context,val){
context.commit("addNum",val) // 调用的时候执行 this.$store.dispatch('a/syncAddNum',3)
}
},
getters: {
addNums(state){
return state.num + "元 " // 调用的时候执行 this.$store.getters['a/addNum2']
}
}
}
export default moduleA

const moduleB = {
namespaced: true,
state: { num:2 },
mutations: {
addNum(state){
state.num += 3
}
},
actions: { },

}
export default moduleB
  1. 启用命名空间后辅助函数 mapState mapGetters mapMutations mapActions 的写法
  • 启用命名空间后的mapState的写法
1
2
3
...mapState({
a1: state => state.b.num,
}),
  • 启用命名空间后的mapGetters的写法
1
2
3
 ...mapGetters({
a2: "b/addNum1"
}),
  • 启用命名空间后的mapMutations的写法
1
...mapMutations({event : "b/addNum"})  // this.event(3) 附带参数的写法
  • 启用命名空间后的mapActions的写法
1
...mapActions({events : "b/syncAddNum"}),  // this.events(4) 附带参数的写法

vuex辅助函数 帮助我们节省代码的语法糖

  1. mapState – 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。(其实就是我们在vuex里面定义的变量 接收的时候还要再定义变量去接收 很麻烦 vuex辅助函数呢 可以帮我们解决这种麻烦)
  2. mapGetters – 将store中的多个getter映射到局部组件的计算属性中
  3. mapMutations 将组件中的 methods 映射为 store.commit 调用。
  4. mapActions 将组件的 methods 映射为 store.dispatch 调用

mapState 的三种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export default {
name: 'home',
components: {
HelloWorld
},
data () {
return {
username:"用户名"
}
},
computed: {
...mapState({
age:state => state.age, // 第一种 箭头函数
num:"num", // 传字符串参数 'num' 等同于 `state => state.num`
name (state) { // 第三种呢 等同于第一种 只不过第三种可以使用this 方便vuex里面的数据结合本地的数据
return this.username + state.name ;
}
})
}
}

mapGetters getters的辅助函数

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
import HelloWorld from '@/components/HelloWorld.vue'
import { mapState , mapGetters , mapMutations , mapActions } from "vuex"
export default {
name: 'home',
components: {
HelloWorld
},
data () {
return {
username:"用户名"
}
},
computed: {
...mapState({
num:"num",
age:state => state.age,
name (state) {
return this.username + state.name ;
}
}),
...mapGetters({
price:"price"
})
}
}

mapMutations mutations的辅助函数的两种写法

1
2
3
4
5
6
7
8
9
methods: {
...mapMutations([ // 第一种
"add", // 调用的时候执行this.add() 映射为 `this.$store.commit('add')`
"adds" // 如果需要提交载荷 直接调用 this.adds(num ) 映射为 `this.$store.commit('adds', num)`
]),
...mapMutations({
add: 'add' // 将 `this.add()` 映射为 `this.$store.commit('add')`
})
}

mapActions actions的辅助函数

1
2
3
4
5
6
7
8
9
10
11
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}

基本使用 demo

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
count: 1
},
mutations: { // commit 提交
// 在 mutations 中定义方法,可以方便的查看全局变量的变化,只有 mutations 中的方法才有权限 修改 state 中的数据
add(state) { // 第一个参数永远是自身的 state 对象
state.count++

// mutations 中,只能执行同步操作,虽然下面这个异步操作在页面中会显示效果,但实际在 vue devtools(vue 调试工具) 中会发现 state.count 的值没有发生改变
// 如果要执行异步操作,把操作放到 actions 中,但 actions 中还是需要使用 mutations 中的函数来间接操作数据,因为只有 mutation 被允许操作 state 的数据
// setTimeout(() => { state.count++ }, 3000)

// console.log(this.state === state) // true
// this.state.count++ // 和上面的两者效果一样。
},
addn(state, step) {
state.count += step
},
sub(state) {
state.count--
},
subn(state, step) {
state.count -= step
}
},
actions: { // 处理异步, dispatch 分发
addAsync(context) { // context 就是 new 出来的 Vuex.Store 对象。 是保存有上面 mutations 中方法参数中的 state 的对象
//
// context.state.count++ // 确实是有效的,但不要这样写!因为这样会让 actions 也有了修改数据的权限,以后不方便查找谁修改了数据
//
// commit 用来触发某个 mutations 中的方法
setTimeout(() => {
// actions 只负责异步操作。在 actions 中, 不能直接修改 state 中的数据,必须通过 commit 来触发某个 mutation,因为只有 mutation 中定义的函数才有权力操作 state 的数据
context.commit('add') // 通过 commit 调用 mutations 中的方法来修改 state 中的数据,这就是上面为什么说需要 mutations 的函数来间接操作。
console.log('AsyncAdd')
}, 1000)
},
addnAsync(context, step) {
setTimeout(() => {
context.commit('addn', step) // 同样,commit 第二个参数表示要传入 addn 的参数
console.log(this) // 和下面异步减法(subnAsync) 打印出来的东西一样 ,那么也就是说,通过 dispatch 和 mapActions 调用的 action 方法是一样的
console.log(context)// 和下面异步减法(subnAsync) 打印出来的东西一样
console.log('AsyncAddn')
}, 1000)
},
subAsync(context) {
setTimeout(() => {
context.commit('sub')
console.log(this)
console.log(context)
console.log('AsyncSub')
})
},
subnAsync(context, step) {
setTimeout(() => {
context.commit('subn', step)
console.log(this) // Store 对象
console.log(context) // 带有 commit dispatch getter state 等方法的对象
console.log('AsyncSubn')
})
}
},
getters: { // 类似于 computed,不会修改 state,仅起到装饰作用,而当 state 发生变化时,会同步发生变化,也就是响应式
showNum(state) {
return `当前最新的数量是 [${state.count}]`
}
},
modules: {
}
})


// 组件中调用 Vuex 里的数据
this.$store.state.count // 通过 $store.state 获取 State 中的值
this.$store.getters.showNum // 通过 $store.getters 获取 Getters 中的值
this.$store.commit('addn', 2) // 通过 commit(函数名) 来调用 store 的 mutations 中定义的函数, 第二个参数表示要传入 addn 函数的参数,有点像 call 方法
this.$store.dispatch('addnAsync', 5) // 通过 dispatch 来触发 actions 中的方法,感觉就像触发了一个自定义事件? 传参,同样的,类似于call,dispatch 第二个参数开始是要传入的参数

// 语法糖 Vuex 提供的辅助函数
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex' // 按需导入 vuex 中的属性
computed: {
...mapState(['count']), // 不能写在 data 中,写在 data 中将无法使用数据,因为这实际上返回的是个函数,函数里面 return 了值, 扩展运算符把 mapState() 返回的对象 按需(在这里需要 count,所以导入 count)导入到了 computed 中
...mapGetters(['showNum']) // 同样,展开 mapGetters ,按需导入,映射到 computed 中
/*
...mapXXXXX 方法还有另外一些写法, 以 ...mapState 为例, 不传入数组而是传入对象
...mapState({
componentCount: 'count', // 重命名, 这样以后用 componentCount 表示 his.$store.state.count', 不能用 count 表示 this.$store.state.count, 上面的 ...mapState(['count']) 相当于 count: 'count' , 不能简写 count, 因为一个是变量, 一个是字符串 'count'
money: function (state) { // 默认传入 $store.state
return state.count + '元'
},
nums: state => state.count + '个' // 写成箭头也完全没有问题
})
*/
},
methods: {
...mapMutations(['sub', 'subn']), // 按需导入 mutations 中的 方法,将需要的 mutations 函数 映射为当前组件的 methods 方法
...mapActions(['subAsync', 'subnAsync'])
}

Modules , 用于解决文件过于臃肿的问题, 把它们拆分成各个对应的子模块 namespace: true

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import cart from 'modules/cart.js'
import goods from 'modules/goods.js'
Vue.use(Vuex)

export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {},
modules: {
cart
}
})

// store/modules/cart.js
export default {
// namespace: true,
state: {
pName: 'cart'
},
mutations: {},
actions: {
asyncAdd(context) {
setTimeout(() => {
context.commit('add') // 不加命名空间时, 会把 goods 和 cart 的 add 都执行一遍
}, 100)
}
},
getters: {
gName(state) {
return state.pName + 'g' // 注意, 这里不写成 state.cart.pName 的原因是 this 指向不同, 当前 this 指向的 Store 对象中, 并没有 cart, 它只表示当前模块, 如果在根 Store 中, 它应该被写成 state.cart.pName
}
}
}

// store/modules/goods.js
export default {
// namespaced: true,
state: {
pName: 'goods'
},
mutations: {
add(state) {
console.log(state)
console.log('add')
}
},
actions: {
asyncAdd(context) {
setTimeout(() => {
context.commit('add') // 不加命名空间时, 调用 asyncAdd, 加上 cart 中的同名异步方法, 会执行两次 context.commit('add'), 也就是会执行四个 add, 两个 goods add(), 两个 cart add()
}, 100)
}
}
/* , getters: {
gName() {
return 'error!' // 不加命名空间时,如果有同名的 getter, 将会报错, 同时将会返回先导入到根模块(store/index.js)的 module 的 getter 值.
}
}
*/
}
// 组件中调用 Vuex 的数据, 不加 namespace 时
/* 调用子模块的 state 的数据不管加不加 namespaced 都需要写成 .state[子模块名].DataName */
this.$store.state.cart.pName // cart
this.$store.state.goods.pName // goods
this.$store.commit('add') // 没有加上 namespaced 时, 如果两个子模块中有同名 mutation, 按顺序调用两个方法, 因为 Vuex 中使用栈结构把方法添加到数组中, 所以两个方法都会被调用
this.$store.dispatch('asyncAdd') // 同样, 会把两个异步操作都执行一次
this.$store.getters.gName // 如果有同名的 getter, 将会报错, 同时将会返回先导入到根模块(store/index.js) 的 module 的 getter 值.

// 子模块加入 namespaced: true 后
this.$store.state.cart.pName // 和不加时的获取方式一致
this.$store.commit('cart/add')
this.$store.dispatch('cart/asyncAdd')
this.$store.getters['cart/gName']

// 加入 namespace 后, 辅助函数的用法,
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
/*
不能写成
...mapState(['cart/pName']
或者
...mapState({
reName: 'cart/pName'
})
*/
...mapState({
reName: function (state) {
console.log(state)
return state.car.pName
}
})

/*
以下三种辅助函数在使用了子模块下, 都只有这种 对象: 字符串 的写法

以下两种写法都不行
...mapMutations(['cart/add'])
因为你使用的时候会写成 @click="cart.add" 或者 @click="cart/add", 都无法被 Vue 识别

...mapMutations({
reAdd: 'cart/add'
})
因为使用 reAdd() 的时候实际上就是 this.$store.commit('cart.add') 打印 // [vuex] unknown mutation type: cart.add
它不会把 'cart.add' 解析成对象($store.cart.add), 而是直接当作方法名字了, 不过, 即使能解析成对象, $store.cart 中也只有 state 里设置的 pName, add 变成了 $store._mutations['cart/add']
*/
...mapMutations({
reAdd: 'cart/add' // 调用 reAdd 相当于 this.$store.commit('cart/add')
})

...mapActions({
reAsyncAdd: 'cart/asyncAdd' // 调用 reAsyncAdd 相当于 this.$store.dispatch('cart/asyncAdd')
})

...mapGetters({
reGName: 'cart/gname' // 获取 reGName 相当于 this.$store.getters['cart/gname']
})
分享到:

评论完整模式加载中...如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理