创建与使用 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;