创建与使用 store

安装 vuex:在 package.json 的 dependencies 添加 “vuex”: “^4.0.0-0”,然后执行 npm i 。
新建文件 app/app.store.ts:

1
2
3
4
5
6
7
import { createStore } from 'vuex';
/**
* 创建store
*/
const store = createStore({});

export default store;

打开入口文件 main.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createApp } from 'vue';
import App from './app/app.vue';
import appStore from './app/app.store';

/**
* 创建应用
*/
const app = createApp(App);

/**
* 应用store
*/
app.use(appStore);

/**
* 挂载应用
*/
app.mount('#app');

state 数据

在应用里需要用的数据,可以放在 store 的 state 这个属性里。在 state 里的数据可以在任何一个组件中使用。演示一下,在 app.store.ts 中:

1
2
3
4
5
6
7
8
9
10
11
import { createStore } from 'vuex';
/**
* 创建store
*/
const store = createStore({
state: {
name: '你好啊',
},
});

export default store;

app.vue 中:

1
2
3
<template>
<h3>{{ $store.state.name }}</h3>
</template>

在组件模板中可以直接使用$store, 在脚本里需要在它前面加上 this。一般可以在组件里定义一个computed属性,然后返回$store 里的 state 里的 name。vuex 提供了一些帮手方法,可以更简单的定义这些 computed 属性:

mapState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<h3>{{ appName }}</h3>
</template>

<script>
import { mapState } from 'vuex'; //引入帮手方法
export default {
data() {
return {};
},
computed: {
// ...mapState(['name']), //这样在组件里就会得到一个叫做name的computed属性,对应的值就是store.state里的name。
...mapState({
//如果你想另外起个名字,可以传一个对象参数,里面设置新的名字。
appName: 'name',
}),
},
};
</script>

<style>
@import './styles/app.css';
</style>

Getters 获取器

在 getters 里面可以根据 state 里已有的数据做一些加工处理,然后再返回新的数据。示范一下,在 app.store.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { createStore } from 'vuex';
/**
* 创建store
*/
const store = createStore({
state: {
name: '你好啊~',
},
getters: {
name(state) {
//这个state就是store里的state
return `~~ ${state.name}`;
},
},
});

export default store;

app.vue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<h3>{{ name }}</h3>
</template>

<script>
import { mapGetters, mapState } from 'vuex';
export default {
data() {
return {};
},
computed: {
...mapGetters(['name']),
},
};
</script>

<style>
@import './styles/app.css';
</style>

Mutations 修改器

如果你想修改 state 里的数据,必须要通过 Mutations 的东西,提交 mutation 用 commit 方法。app.store.ts 中定义 mutation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { createStore } from 'vuex';
/**
* 创建store
*/
const store = createStore({
state: {
name: '你好啊',
},
getters: {
name(state) {
//这个state就是store里的state
return `~~ ${state.name}`;
},
},
mutations: {
setName(state, data) {
//state就是store里的state,data就是使用这个mutation的时候传递的那个值
state.name = data;
},
},
});

export default store;

组件中使用:

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
<template>
<h3 @click="onClickName">{{ name }}</h3> //新增一个点击事件
</template>

<script>
import { mapGetters, mapState } from 'vuex';
export default {
data() {
return {};
},
computed: {
...mapGetters(['name']),
},
methods: {
onClickName() {
if (this.$store.state.name === '你好啊') {
this.$store.commit('setName', '哈哈你好'); //这里使用commit提交mutation,第一个参数是mutation的名字,第二个是要修改的值
} else {
this.$store.commit('setName', '嘻嘻你好');
}
},
},
};
</script>

<style>
@import './styles/app.css';
</style>

mapMutations

使用这个帮手方法可以把 store 里的 mutation 映射成组件里的一个方法。在组件中:

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
<template>
<h3 @click="onClickName">{{ name }}</h3>
</template>

<script>
import { mapGetters, mapMutations, mapState } from 'vuex'; //引入
export default {
data() {
return {};
},
computed: {
...mapGetters(['name']),
},
methods: {
...mapMutations(['setName']), //用mapmutation的方法,如果想换个名字可以传入对象
onClickName() {
if (this.$store.state.name === '你好啊') {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('嘻嘻你好呀'); //使用
} else {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('哈哈哈哈哦');
}
},
},
};
</script>

<style>
@import './styles/app.css';
</style>

actions 动作

用 actions 处理应用里的数据,比如从接口中获取到数据,或者把数据发送到服务端。在 actions 里面可以 commit store 里的 mutation,来修改 state。比如你要从接口获取到一些数据,可以先在 state 里定义一个表示这个数据的 state,然后再定义一个修改 state 用的 mutation,再去定义一个获取数据用的 action,在这个 action 里面你可以请求接口,获取数据,得到数据后可以用 mutation 来修改 state。
app.store.ts:

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
import { createStore } from 'vuex';
/**
* 创建store
*/
const store = createStore({
state: {
name: '', // 先设置为空
},
getters: {
name(state) {
//这个state就是store里的state
return `~~ ${state.name}`;
},
},
mutations: {
setName(state, data) {
//state就是store里的state,data就是使用这个mutation的时候传递的那个值
state.name = data;
},
},
actions: {
getName(context) {
//在store里的actions方法都有一个context参数,它上面提供了一些东西比如store里的state,getters,还有commit等方法

// 假设请求了接口,拿到了数据后
const name = '玄黄';
context.commit('setName', name); //提交setname这个mutation
},
},
});

export default store;

组件中,在组件创建后用 dispatch 触发 action:

1
2
3
created() {
this.$store.dispatch('getName'); //用dispatch这个方法派发执行一个action
},

mapActions

可以通过 mapActions 把 store 里定义的 action 映射成组件里的方法。

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
<template>
<h3 @click="onClickName">{{ name }}</h3>
</template>

<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex'; //引入帮手方法
export default {
data() {
return {};
},
created() {
// this.$store.dispatch('getName'); //用dispatch这个方法派发一个action
this.getName(); // 然后在这里使用
},
computed: {
...mapGetters(['name']),
},
methods: {
...mapMutations(['setName']),
...mapActions({ //使用帮手方法,如果想起名传对象,否则传数组
getName: 'getName',
}),
onClickName() {
if (this.$store.state.name === '你好啊') {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('嘻嘻你好呀');
} else {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('哈哈哈哈哦');
}
},
},
};
</script>

<style>
@import './styles/app.css';
</style>

actions 里的 context 参数
打印 context。
commit:提交 mutation
dispatch:在动作里可以使用这个方法来派发其他的动作
getters:获取器
rootGetters:找到所有 store 里的 getters
rootState:所有 store 里面的 state
state:当前 store 的 state

如果你只想使用 context 里的某一个方法,可以结构出来。比如:

1
2
3
4
5
6
7
8
9
actions: {
getName(context) {
//在store里的actions方法都有一个context参数,它上面提供了一些东西比如store里的state,getters,还有commit等方法

// 假设请求了接口,拿到了数据后
const name = '玄黄';
context.commit('setName', name);
},
}

可以写成:

1
2
3
4
5
6
7
8
9
actions: {
getName({ commit }) {
//在store里的actions方法都有一个context参数,它上面提供了一些东西比如store里的state,getters,还有commit等方法

// 假设请求了接口,拿到了数据后
const name = '玄黄';
commit('setName', name);
},
}

加载状态 loading

在 action 中经常包含一些异步的动作,也就是执行动作后不能马上得到数据,等待的时候可以在页面显示一个加载状态,拿到数据后再去掉状态显示内容。示范一下,打开 app.store.ts:

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
import { createStore } from 'vuex';
/**
* 创建store
*/
const store = createStore({
state: {
name: '',
loading: false, //定义加载状态
},
getters: {
name(state) {
//这个state就是store里的state
return `~~ ${state.name}`;
},
},
mutations: {
setName(state, data) {
//state就是store里的state,data就是使用这个mutation的时候传递的那个值
state.name = data;
},
setLoading(state, data) { //修改状态的方法
state.loading = data;
},
},
actions: {
getName({ commit }) {
commit('setLoading', true);
setTimeout(() => { //模拟异步
const name = '哈哈哈';
commit('setName', name);
commit('setLoading'); //提交修改方法
}, 2000);
},
},
});

export default store;

组件中使用:

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
<template>
<h3 @click="onClickName">{{ name }}</h3>
<span v-if="loading">加载中</span> //添加加载中标签
</template>

<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
export default {
data() {
return {};
},
created() {
// this.$store.dispatch('getName'); //用dispatch这个方法派发一个action
this.getName();
},
computed: {
...mapGetters(['name']),
...mapState(['loading']), //使用帮手方法将loading映射到这个组件
},
methods: {
...mapMutations(['setName']),
...mapActions({
getName: 'getName',
}),
onClickName() {
if (this.$store.state.name === '你好啊') {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('嘻嘻你好呀');
} else {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('哈哈哈哈哦');
}
},
},
};
</script>

<style>
@import './styles/app.css';
</style>

vuex store 模块

通常情况下不能把所有东西都放在一个 store 里面,可以根据需求创建对应的 store 模块。比如要把跟用户相关的东西放在一个 store 里面。
新建 user.store.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { RootState } from '@/app/app.store';
import { Module } from 'vuex';
export interface UserState {
currentUser: string;
}
const store: Module<UserState, RootState> = {
state: {
currentUser: '',
},
mutations: {
setCurrentUser(state, data) {
state.currentUser = data;
},
},
actions: {
getCurrentUser(context) {
const user = '哈嘻';
context.commit('setCurrentUser', user);
},
},
};

export default store;

打开 app.store.ts:

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
import { createStore } from 'vuex';
import user, { UserState } from '@/user/user.store'; //引入
export interface RootState { //定义类型
name: string;
loading: boolean;
user?: UserState;
}
/**
* 创建store
*/
const store = createStore({
state: {
name: '',
loading: false,
},
getters: {
name(state) {
//这个state就是store里的state
return `~~ ${state.name}`;
},
},
mutations: {
setName(state, data) {
//state就是store里的state,data就是使用这个mutation的时候传递的那个值
state.name = data;
},
setLoading(state, data) {
state.loading = data;
},
},
actions: {
getName({ commit, rootState }) { //定义action动作
console.log(rootState);
commit('setLoading', true);
setTimeout(() => {
const name = '哈哈哈';
commit('setName', name);
commit('setLoading');
}, 2000);
},
},
modules: {
user,
},
});

export default store;

app.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
36
37
38
39
40
41
42
43
44
45
<template>
<h3 @click="onClickName">
{{ name }}
<span v-if="loading">加载中</span>
</h3>
{{ user.currentUser }} //输出
</template>

<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
export default {
data() {
return {};
},
created() {
// this.$store.dispatch('getName'); //用dispatch这个方法派发一个action
this.getName();
this.getCurrentUser(); //调用
},
computed: {
...mapGetters(['name']),
...mapState(['loading', 'user']), //添加user
},
methods: {
...mapMutations(['setName']),
...mapActions({
getName: 'getName',
getCurrentUser: 'getCurrentUser', //添加getCurrentUser
}),
onClickName() {
if (this.$store.state.name === '你好啊') {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('嘻嘻你好呀');
} else {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('哈哈哈哈哦');
}
},
},
};
</script>

<style>
@import './styles/app.css';
</style>

vuex store 模块的命名空间

在创建 store 模块的时候可以设置一下让这个模块使用命名空间。
user.store.ts:

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
import { RootState } from '@/app/app.store';
import { Module } from 'vuex';
export interface UserState {
currentUser: string;
}
const store: Module<UserState, RootState> = {
namespaced: true, //添加namespaced属性设置为true就可以了
state: {
currentUser: '',
},
getters: {
currentUser(state) {
return state.currentUser;
},
},
mutations: {
setCurrentUser(state, data) {
state.currentUser = data;
},
},
actions: {
getCurrentUser(context) {
const user = '哈嘻';
context.commit('setCurrentUser', user);
},
},
};

export default store;

app.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
36
37
38
39
40
41
42
43
44
45
46
47
48
<template>
<h3 @click="onClickName">
{{ name }}
<span v-if="loading">加载中</span>
</h3>
{{ currentUser }}
</template>

<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
export default {
data() {
return {};
},
created() {
// this.$store.dispatch('getName'); //用dispatch这个方法派发一个action
this.getName();
this.getCurrentUser();
},
computed: {
...mapGetters({
name: 'name',
currentUser: 'user/currentUser', //使用的时候前面要加user/
}),
...mapState(['loading', 'user']),
},
methods: {
...mapMutations(['setName']),
...mapActions({
getName: 'getName',
getCurrentUser: 'user/getCurrentUser', //使用的时候前面要加user/
}),
onClickName() {
if (this.$store.state.name === '你好啊') {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('嘻嘻你好呀');
} else {
// this.$store.commit('setName', '嘻嘻你好');
this.setName('哈哈哈哈哦');
}
},
},
};
</script>

<style>
@import './styles/app.css';
</style>

创建与使用 store 插件

应用的 store 发生变化的时候可以通过 store 的插件来选择做一些事情。

自带的插件:

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
import { createStore, createLogger } from 'vuex';   //引入createLogger
import user, { UserState } from '@/user/user.store';

export interface RootState {
name: string;
loading: boolean;
user?: UserState;
}
/**
* 创建store
*/
const store = createStore({
state: {
name: '',
loading: false,
},
getters: {
name(state) {
//这个state就是store里的state
return `~~ ${state.name}`;
},
},
mutations: {
setName(state, data) {
//state就是store里的state,data就是使用这个mutation的时候传递的那个值
state.name = data;
},
setLoading(state, data) {
state.loading = data;
},
},
actions: {
getName({ commit, rootState }) {
console.log(rootState);
commit('setLoading', true);
setTimeout(() => {
const name = '哈哈哈';
commit('setName', name);
commit('setLoading');
}, 2000);
},
},
modules: {
user,
},

plugins: [createLogger()], //使用插件
});

export default store;

也可以自定义插件。新建文件 app/app.plugin.ts:

1
2
3
4
5
6
7
8
import { Plugin } from 'vuex';
import { RootState } from '@/app/app.store';

export const logger: Plugin<RootState> = store => {
store.subscribe((mutation, state) => {
console.log(mutation, state);
});
};

然后在 app.store.ts 中使用:

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
import { createStore, createLogger } from 'vuex';
import user, { UserState } from '@/user/user.store';
import { logger } from './app.plugin';

export interface RootState {
name: string;
loading: boolean;
user?: UserState;
}
/**
* 创建store
*/
const store = createStore({
state: {
name: '',
loading: false,
},
getters: {
name(state) {
//这个state就是store里的state
return `~~ ${state.name}`;
},
},
mutations: {
setName(state, data) {
//state就是store里的state,data就是使用这个mutation的时候传递的那个值
state.name = data;
},
setLoading(state, data) {
state.loading = data;
},
},
actions: {
getName({ commit, rootState }) {
console.log(rootState);
commit('setLoading', true);
setTimeout(() => {
const name = '哈哈哈';
commit('setName', name);
commit('setLoading');
}, 2000);
},
},
modules: {
user,
},

plugins: [createLogger(), logger],
});

export default store;