Centralized State Management for Vue.js.

项目初始化

初始化项目,项目目录为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
│  .gitignore
│ babel.config.js
│ package-lock.json
│ package.json
│ README.md

├─node_modules
├─public
│ favicon.ico
│ index.html

└─src
│ App.vue
│ main.js

├─assets
│ logo.png

├─components
│ HelloWorld.vue

└─store
index.js

初始App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div id="app">
<HelloWorld />
</div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
name: "App",
components: {
HelloWorld,
},
};
</script>

<style>
</style>

初始HelloWorld.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div class="hello">
<h1>Vuex 示例</h1>
</div>
</template>

<script>
export default {
name: "HelloWorld",
};
</script>

<style scoped>
</style>

初始化的Vuex/src/store目录下的index.js:

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

Vue.use(Vuex)

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

此时页面显示效果:

image-20201104165246857

state

在组件中引入Vuex中的状态

  1. 直接引入

    直接修改HelloWorld.vue的h标签

    1
    <h1>{{ this.$store.state.msg }}</h1>

    index.js中加入

    1
    2
    3
    state: {
    msg: 'this is vuex --- Vuex'
    },

    显示结果为:

    image-20201105084551745

  2. 计算属性引入

    HelloWorld.vue中增加计算属性,h标签和上面保持一致

    1
    2
    3
    4
    5
    6
    7
    8
    export default {
    name: "HelloWorld",
    computed: {
    msg() {
    return this.$store.state.msg
    }
    }
    };

    显示结果为:

    image-20201105085912982

  3. mapState辅助函数

    修改HelloWorld.vue

    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
    <template>
    <div class="hello">
    <h1>{{ msg }}</h1>
    <h1>{{ msgAlias }}</h1>
    <h1>{{ msgAddLocation }}</h1>
    </div>
    </template>

    <script>
    import {
    mapState
    } from 'vuex'
    export default {
    name: "HelloWorld",
    data(){
    return{
    localMsg:'local'
    }
    },
    computed: mapState({
    msg: state => state.msg,
    msgAlias: 'msg',
    msgAddLocation(state) {
    return state.msg+this.localMsg
    }
    })
    };
    </script>

    显示结果为:

    image-20201105091214317

    当映射的计算属性名称和state的子节点名称相同,可以给mapState传一个字符串数组

    1
    2
    3
    computed: mapState([
    'msg'
    ])

    此时显示结果为:

    image-20201105091719949

  1. 对象展开运算符...mapState

    1
    2
    3
    4
    5
    6
    computed:{
    ...mapState(['msg']),
    ...mapState({
    message:'msg'
    })
    }

Getter

getter可以视为store的计算属性,其第一个参数为state

修改index.js

1
2
3
4
5
6
7
8
9
10
11
export default new Vuex.Store({
state: {
msg: 'this is vuex --- Vuex by mapState',
name: 'wonderful '
},
getters: {
getMsg: state => state.msg,
getName: (state, getters) => state.name + getters.getMsg
},
//......
})

修改HelloWorld.vue

1
2
3
4
5
computed: {
msg() {
return this.$store.getters.getName
}
}

显示结果为:

image-20201105093838536

mapGetters辅助函数

修改HelloWorld.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h1>{{ name }}</h1>
</div>
</template>

<script>
import {
mapGetters
} from 'vuex'
export default {
name: "HelloWorld",
computed: mapGetters({
msg: 'getMsg',
name: 'getName'
})
};
</script>

<style scoped>
</style>

使用对象展开运算符...mapGetters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div class="hello">
<h1>{{ getMsg }}</h1>
<h1>{{ getName }}</h1>
</div>
</template>

<script>
import {
mapGetters
} from 'vuex'
export default {
name: "HelloWorld",
computed: {
...mapGetters(['getMsg', 'getName'])
}
};
</script>

<style scoped>
</style>

上面两种写法的显示结果为:

image-20201105095102215

Mutation

更改Vuexstore中状态的唯一方法是提交mutation

修改的HelloWorld.vue

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
<template>
<div class="hello">
<h1 @mouseover="onmouseover">{{ count }}</h1>
</div>
</template>

<script>
import {
mapGetters
} from 'vuex'
export default {
name: "HelloWorld",
computed: mapGetters({
msg: 'getMsg',
name: 'getName',
count: 'getCount'
}),
methods:{
onmouseover(){
this.$store.commit('increment')
},
}
};
</script>

<style scoped>
</style>

修改后的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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
msg: 'this is vuex --- Vuex by mapState',
name: 'wonderful ',
count: 1
},
getters: {
getMsg: state => state.msg,
getName: (state, getters) => state.name + getters.getMsg,
getCount: state => state.count
},
mutations: {
increment(state) {
state.count++
}
},
actions: {},
modules: {}
})

此时鼠标在数字上移动时,数字会递增。

在提交commit时也可以传入额外的参数,即mutation的载荷(payload)

对上述例子进行少许改动:

HelloWorld.vue

1
2
3
4
5
methods:{
onmouseover(){
this.$store.commit('increment',10)
},
}

index.js

1
2
3
4
5
mutations: {
increment(state, payload) {
state.count += payload
}
},

对于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
<template>
<div class="hello">
<h1 @mouseover="onmouseover">{{ count+' '+name }}</h1>
</div>
</template>

<script>
import {
mapGetters
} from 'vuex'
export default {
name: "HelloWorld",
computed: mapGetters({
msg: 'getMsg',
name: 'getName',
count: 'getCount'
}),
methods: {
onmouseover() {
this.$store.commit('increment', {
count: 10,
msg: 'new',
name: 'thanks'
})
},
}
};
</script>

<style scoped>
</style>

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
msg: 'msg',
name: 'name ',
count: 1
},
getters: {
getMsg: state => state.msg,
getName: (state, getters) => state.name + getters.getMsg,
getCount: state => state.count
},
mutations: {
increment(state, payload) {
state.count += payload.count
state.msg += payload.msg
state.name += payload.name
}
},
actions: {},
modules: {}
})

mapMutations辅助函数

修改HelloWorld.vue

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 class="hello">
<h1 @mouseover="onmouseover">{{ count }}</h1>
</div>
</template>

<script>
import {
mapGetters,
mapMutations
} from 'vuex'
export default {
name: "HelloWorld",
computed: mapGetters({
msg: 'getMsg',
name: 'getName',
count: 'getCount'
}),
methods: {
onmouseover() {
this.add({
count: 10
})
},
...mapMutations(['increment']),
...mapMutations({
add:'increment'//将increment映射为add
})
}
};
</script>

<style scoped>
</style>

Vuex 中,mutation都是同步事务,store.commit('increment')中,任何由increment导致的状态变更都应该在此刻完成。

Action

Action类似于mutation,不同在于:

  • Action提交的是mutation,而不是直接变更状态
  • Action可以包含任意异步操作

注册一个Action

1
2
3
4
5
actions: {
increment(context) {
context.commit('increment')
}
}

上述写法等同于以下写法(ES2015 的 参数解构):

1
2
3
4
5
actions: {
increment({commit}) {
commit('increment')
}
}

Action函数接受一个与store实例具有相同方法和属性的context对象,因此可以调用context.commit提交mutation,或者通过context.statecontext.getters来获取stategetters

context对象并不是store实例本身

Action通过store.dispatch方法触发:

store.dispatch('increment')

修改HelloWorld.vue中的mouseover函数:

1
2
3
onmouseover() {
this.$store.dispatch('increment')
}

不同于mutation,我们可以在Action中执行异步操作:

1
2
3
4
5
increment(context) {
setTimeout(() => {
context.commit('increment')
}, 1000)
}

Actions支持同样的载荷方式和对象方式进行分发:

1
2
3
4
5
6
7
8
9
10
// 以载荷形式分发
store.dispatch('increment', {
amount: 10
})

// 以对象形式分发
store.dispatch({
type: 'increment',
amount: 10
})

mapActions辅助函数

修改HelloWorld.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import {
mapGetters,
mapMutations,
mapActions
} from 'vuex'
export default {
name: "HelloWorld",
computed: mapGetters({
msg: 'getMsg',
name: 'getName',
count: 'getCount'
}),
methods: {
onmouseover() {
this.increment()
//this.add()
},
...mapActions(['increment']),
...mapActions({
add: 'increment'
})
}
};

Action组合

Module