Vuex

1、什么是Vuex

  • 概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对于Vue应用中多个组件间的共享状态进行集中管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信

2、安装与配置vuex

2.1、安装

  1. 安装:npm install vuex / yarn add vuex
  2. 注意:vue2.x需安装vuex3版本,vue3.x需安装vuex4版本

2.2、配置

  1. 在src路径下创建store文件夹,然后创建index.js文件,文件内容如下:

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    // 定义值
    const state = {};
    
    // 修饰器
    const getters = {};
    
    // 修改值
    const mutations = {};
    
    // 异步修改值
    const actions = {};
    
    export default new Vuex.Store({
      state,
      getters,
      mutations,
      actions,
    });
    
  2. 修改main.js:

    import Vue from 'vue'
    import App from './App.vue'
    import store from './store'; // 引入我们前面导出的store对象
    
    Vue.config.productionTip = false
    
    new Vue({
      store,
      render: h => h(App),
    }).$mount('#app')
    
    
    1. 组件中使用:
    <template>
        <div></div>
    </template>
    
    <script>
        export default {
            mounted() {
                // 使用this.$store.state.XXX可以直接访问到仓库中的状态
                console.log(this.$store.state.name); 
            }
        }
    </script>
    
    

3、使用vuex

3.1、…mapState

  • 官方建议我们以上操作this.$store.state.XXX最好放在计算属性中,当然,我也建议你这么使用,这样可以让你的代码看起来更优雅一些,就像这样:

    export default {
        mounted() {
            console.log(this.getName); 
        },
        computed:{
            getName() {
                return this.$store.state.name;
            }
        },
    }
    
  • 是不是每次都写this.$store.state.XXX让你感到厌烦,你实在不想写这个东西怎么办,当然有解决方案,就像下面这样:

    <script>
    import { mapState } from 'vuex'; // 从vuex中导入mapState
    export default {
        mounted() {
            console.log(this.name);
        },
        computed: {
            ...mapState(['name']), // 经过解构后,自动就添加到了计算属性中,此时就可以直接像访问计算属性一样访问它
        },
    }
    </script>
    
    
  • 你甚至可以在解构的时候给它赋别名,取外号,就像这样:

    ...mapState({ aliasName: 'name' }),  // 赋别名的话,这里接收对象,而不是数组
    
    

3.2、修饰器:Getter

  1. 什么是修饰器?以下假设一个场景:

    • 设想一个场景,你已经将store中的name展示到页面上了,而且是很多页面都展示了,此时产品经理过来找事儿:
    • 产品经理:所有的name前面都要加上“hello”!

    这时候,你第一想到的是怎么加呢,emm…在每个页面上,使用this.$store.state.name获取到值之后,进行遍历,前面追加"hello"即可。

    错!这样很不好,原因如下:

    • 第一,假如你在A、B、C三个页面都用到了name,那么你要在这A、B、C三个页面都修改一遍,多个页面你就要加很多遍这个方法,造成代码冗余,很不好;
    • 第二,假如下次产品经理让你把 “hello” 改成 “fuck” 的时候,你又得把三个页面都改一遍,这时候你只能抽自己的脸了…

    那么应该如何?

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    const state = {
      name: '张三',
    };
    
    const getters = {
      getMessage(state) { // 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
        return `hello${state.name}`;
      }
    };
    
    const mutations = {};
    
    const actions = {};
    
    export default new Vuex.Store({
      state,
      getters,
      mutations,
      actions,
    });
    
    

    在组件中使用:

    export default {
        mounted() {
            // 注意不是$store.state了,而是$store.getters
            console.log(this.$store.state.name);
            console.log(this.$store.getters.getMessage);
        }
    }
    

3.2.1、…mapGetters

  • 官方建议: 是不是每次都写this.$store.getters.XXX让你感到厌烦,你实在不想写这个东西怎么办,当然有解决方案,官方建议我们可以使用mapGetters去解构到计算属性中,就像使用mapState一样,就可以直接使用this调用了,就像下面这样:

    <script>
    import { mapState, mapGetters } from 'vuex';
    export default {
        mounted() {
            console.log(this.name);
            console.log(this.getMessage);
        },
        computed: {
            ...mapState(['name']),
            ...mapGetters(['getMessage']),
        },
    }
    </script>
    
  • 当然,和mapState一样你也可以取别名,取外号,就像下面这样:

    ...mapGetters({ aliasName: 'getMessage' }),  // 赋别名的话,这里接收对象,而不是数组
    

OK,当你看到这里,你已经成功的把Getter用起来了,你也能明白在什么时候应该用到getters,你可以通过计算属性访问(缓存),也可以通过方法访问(不缓存),你甚至可以在getters的方法里面再调用getters方法,当然你也实现了像state那样,使用mapGetters解构到计算属性中,这样你就可以很方便的使用getters啦!

读取值的操作我们有 “原生读(state)” 和 “修饰读(getters)”,接下来就要介绍怎么修改值了!

3.3、修改值:Mutation

3.3.1、修改state中的值

  1. 修改store/index.js

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    // 定义值
    const state = {
      name: '张三',
    };
    
    // 修饰器
    const getters = {
      getMessage(state) { // 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
        return `hello${state.name}`;
      }
    };
    
    // 修改值
    const mutations = {
      setNumber(state) { // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
        state.number = 5;
      }
    };
    
    // 异步修改值
    const actions = {};
    
    export default new Vuex.Store({
      state,
      getters,
      mutations,
      actions,
    });
    
  2. 组件中使用

    <script>
    export default {
        mounted() {
            console.log(`旧值:${this.$store.state.number}`);
            this.$store.commit('setNumber');
            console.log(`新值:${this.$store.state.number}`);
        },
    }
    </script>
    

3.3.2、给mutations中的方法传参

  1. 修改store/index.js

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    // 定义值
    const state = {
      name: '张三',
    };
    
    // 修饰器
    const getters = {
      getMessage(state) { // 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
        return `hello${state.name}`;
      }
    };
    
    // 修改值
    const mutations = {
      setNumber(state) { // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
        state.number = 5;
      },
      setNumberIsWhat(state, number) { // 增加一个带参数的mutations方法
        state.number = number;
      },
    };
    
    // 异步修改值
    const actions = {};
    
    export default new Vuex.Store({
      state,
      getters,
      mutations,
      actions,
    });
    
    
  2. 组件中使用

    <script>
    export default {
        mounted() {
            console.log(`旧值:${this.$store.state.number}`);
            this.$store.commit('setNumberIsWhat', 666);
            console.log(`新值:${this.$store.state.number}`);
        },
    }
    </script>
    
  3. 注意:上面的这种传参的方式虽然可以达到目的,但是并不推荐,官方建议传递一个对象进去,这样看起来更美观,对象的名字你可以随意命名,但我们一般命名为payload,代码如下:

    • 修改store/index.js

      // 修改值
      const mutations = {
        setNumber(state) { // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
          state.number = 5;
        },
        setNumberIsWhat(state, payload) { // 增加一个带参数的mutations方法,并且官方建议payload为一个对象
          state.number = payload.number;
        },
      };
      
    • 组件中使用

      <script>
      export default {
          mounted() {
              console.log(`旧值:${this.$store.state.number}`);
              this.$store.commit('setNumberIsWhat', { number: 666 });  // 调用的时候也需要传递一个对象
              console.log(`新值:${this.$store.state.number}`);
          },
      }
      </script>
      
    • 此时可以得到和之前一样的效果,并且代码更加美观!

3.3.3、…mapMutations

  • 官方建议:就像最开始的mapState和mapGetters一样,我们在组件中可以使用mapMutations以代替this.$store.commit(‘XXX’),是不是很方便呢?

    <script>
    import { mapMutations } from 'vuex';
    export default {
        mounted() {
            this.setNumberIsWhat({ number: 999 });
        },
        methods: {   // 注意,mapMutations是解构到methods里面的,而不是计算属性了
            ...mapMutations(['setNumberIsWhat']),
        },
    }
    </script>
    
  • 当然你也可以给它叫别名,取外号,就像这样:

    methods:{
        ...mapMutations({ setNumberIsAlias: 'setNumberIsWhat' }),   // 赋别名的话,这里接收对象,而不是数组
    }
    

3.3.4、注意事项

  • Mutations里面的函数必须是同步操作,不能包含异步操作!

3.4、异步操作:Actions

  • Actions存在的意义是假设你在修改state的时候有异步操作,vuex作者不希望你将异步操作放在Mutations中,所以就给你设置了一个区域,让你放异步操作,这就是Actions

3.4.1、异步修改state中的值

  1. 修改store/index.js

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    // 定义值
    const state = {
      name: '张三',
    };
    
    // 修饰器
    const getters = {
      getMessage(state) { // 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
        return `hello${state.name}`;
      }
    };
    
    // 修改值
    const mutations = {
      setNumber(state) { // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
        state.number = 5;
      },
      setNumberIsWhat(state, payload) { // 增加一个带参数的mutations方法,并且官方建议payload为一个对象
        state.number = payload.number;
      },
    };
    
    // 异步修改值
    const actions = {
      setNum(content) { // 增加setNum方法,默认第一个参数是content,其值是复制的一份store
        return new Promise(resolve => { // 我们模拟一个异步操作,1秒后修改number为888
          setTimeout(() => {
            content.commit('setNumberIsWhat', {
              number: 888
            });
            resolve();
          }, 1000);
        });
      }
    };
    
    export default new Vuex.Store({
      state,
      getters,
      mutations,
      actions,
    });
    
    
  2. 组件中使用

    async mounted() {
        console.log(`旧值:${this.$store.state.number}`);
        await this.$store.dispatch('setNum');
        console.log(`新值:${this.$store.state.number}`);
    },
    
  3. 官方建议:在store/index.js中的actions里面,方法的形参可以直接将commit解构出来,这样可以方便后续操作:

    // 异步修改值
    const actions = {
        setNum({ commit }) { // 增加payload参数
            return new Promise(resolve => {
                setTimeout(() => {
                    commit('setNumberIsWhat', {
                        number: 888
                    });
                    resolve();
                }, 1000);
            });
        }
    };
    
  4. 看了例子,是不是明白了,action就是去提交mutation的,什么异步操作都在action中消化了,最后再去提交mutation的。

3.4.2、给action中的方法传参

  1. 修改store/index.js

    // 异步修改值
    const actions = {
        setNum(content, payload) { // 增加payload参数
            return new Promise(resolve => {
                setTimeout(() => {
                    content.commit('setNumberIsWhat', {
                        number: payload.number
                    });
                    resolve();
                }, 1000);
            });
        }
    };
    
  2. 组件中使用

    async mounted() {
        console.log(`旧值:${this.$store.state.number}`);
        await this.$store.dispatch('setNum', { number: 611 });
        console.log(`新值:${this.$store.state.number}`);
    },
    

3.4.3、…mapActions

  • 官方建议:你如果不想一直使用this.$store.dispatch(‘XXX’)这样的写法调用action,你可以采用mapActions的方式,把相关的actions解构到methods中,用this直接调用:

    <script>
    import { mapActions } from 'vuex';
    export default {
        methods: {
            ...mapActions(['setNum']),   // 就像这样,解构到methods中
        },
        async mounted() {
            await this.setNum({ number: 123 });  // 直接这样调用即可
        },
    }
    </script>
    
  • 当然,你也可以取别名,取外号,就像下面这样:

    ...mapActions({ setNumAlias: 'setNum' }),   // 赋别名的话,这里接收对象,而不是数组
    

作者:三年没洗澡
链接:https://juejin.cn/post/6928468842377117709
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4、vuex模块化

  1. 修改store/index.js

      import Vue from 'vue';
      import Vuex from 'vuex';
    
      Vue.use(Vuex);
    
      // 模块1
    const aaa = {
        // 开启命名空间
        namespaced: true,
        // 定义值
        state: {
            name:'张三'
        },
    
        // 修饰器
        getters: {},
    
        // 修改值
        mutations: {},
    
        // 异步修改值
        actions: {},
      }
    
      // 模块2
    const bbb = {
        // 开启命名空间
        namespaced: true,
        // 定义值
        state: {
            name:'李四'
        },
    
        // 修饰器
        getters: {},
    
        // 修改值
        mutations: {},
    
        // 异步修改值
        actions: {},
      }
    
      export default new Vuex.Store({
        modules: {
          aaa,
          bbb
        }
      });
    
  2. 组件中使用

    mounted(){
        console.log(this.aaa); //张三
        console.log(this.bbb); //李四
      },
      computed:{
        // 第一个值为模块名,第二个值为要取的属性名
        ...mapState('aaa',{aaa: 'name'}),
        ...mapState('bbb',{bbb: 'name'}),
      }
    

Vuex
http://localhost:8090//archives/vuex
作者
龟龟
发布于
2020年12月18日
更新于
2024年08月28日
许可协议