美文网首页Vue
Vuex基本使用

Vuex基本使用

作者: h2coder | 来源:发表于2023-08-20 17:56 被阅读0次

    依赖版本

    • vue:2.6.14
    • vuex:3.6.2

    配置Vuex

    • Vuex主要有5个配置部分,分别为
      • state,状态,也就是要共享的数据
      • mutations,同步修改state状态的函数
      • actions,异步修改state状态的函数,其实就是异步结束后,调用mutations的函数,实现修改state数据
      • getters,类似Vue组件的计算属性,可以对state状态数据进行统计、合并多个state为一个新的响应式变量
      • modules,模块,Vuex支持定义模块,每个模块都有自己的state、mutations、actions、getters

    创建Vuex实例(store\index.js)

    // Vuex相关配置,注意:Vue2的Vuex版本是3版本
    import Vue from 'vue';
    import Vuex from 'vuex';
    
    // 注册Vuex插件
    Vue.use(Vuex);
    
    // 创建仓库,管理数据
    const store = new Vuex.Store({
        // 严格模式,不能直接修改state的数据
        // 默认为false,是可以直接修改的,但我们都要遵循单向数据流,不允许组件中直接修改状态,设置为true,如果直接修改数据,就会报错!!
        strict: true,
        // 状态,存放数据
        state: {
        },
        // 同步修改state的函数,要求修改state,必须通过 mutation 函数
        mutations: {
        },
        actions: {
            // 异步修改数据,异步修改数据必须通过调用 mutation 函数,也就是在 mutation 上套了一层action
        },
        getters: {
            // 类似计算属性,通过计算某个state,或处理多个state属性来得到一个结果
        },
        // 模块
        modules: {
        }
    });
    
    // 默认导出
    export default store
    

    使用Vuex(main.js)

    import Vue from 'vue'
    import App from './App.vue'
    
    // 导入Vuex的仓库实例
    // import store from './store/index.js' // 完整路径写法
    // import store from './store/index'// js后缀可以省略
    import store from './store' // 如果js文件的名称为index,index 也可以省略
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
      // 挂载到 Vue 的实例上,以后就可以通过Vue的实例,通过 $store 获取到仓库的实例
      store
    }).$mount('#app')
    

    开始使用

    3种获取Vuex实例的方式

    • 在state中,定义状态变量
    const store = new Vuex.Store({
        // 状态,存放数据
        state: {
            uname: '万大爷'
        },
    });
    

    在Vue组件模板中

    <div>
        {{ $store.state.uname }}
    </div>
    

    在Vue组件的js脚本中

    export default {
    name: 'App',
    components: {
        HelloWorld
    },
    created() {
        console.log(this.$store)
        // 通过Vue实例上$store,适合在Vue组件的环境下使用
        console.log(this.$store.state.uname)
    }
    

    在单独的js文件中

    // 单独引入store,适合在非Vue组件环境下的时候使用,例如在 xx.js 中
    import store from '@/store'
    
    console.log(store.state.uname);
    

    辅助函数

    我们在vuex的state、mutations、actions、getters中,配置变量和函数,都在vuex的JS对象里面,想要获取某个变量或函数,需要一层层调用去取,非常麻烦。

    所以vuex提供了4个辅助映射函数,将state、getters的状态变量,映射到组件的计算属性中,将mutations、actions的函数映射到组件的函数中,方便我们通过this.变量名this.函数名来调用

    • mapState:映射state的辅助函数,将vuex中state的数据,映射到 computed 计算属性中
    • mapMutations:映射mutation的辅助函数,映射到 methods 中
    • mapActions:映射action的辅助函数,映射到 methods 中
    • mapGetters:映射getters的辅助函数,映射到 computed 计算属性中

    核心概念 - state

    state,状态,也就是数据,需要多个组件之间共享的数据

    简单使用

    • 在state中,定义状态变量
    const store = new Vuex.Store({
        // 状态,存放数据
        state: {
            uname: '万大爷',
            count: 100,
        },
    });
    
    • 在页面中,通过插值表达式使用状态变量
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 直接使用store实例,写起来太繁琐 -->
          uname:{{ $store.state.uname }} count:{{ $store.state.count }}
        </div>
      </div>
    </template>
    

    优化,使用辅助映射函数

    • vuex提供了辅助映射函数,来帮助我们将state状态数据,映射到vue组件的computed计算属性中,然后我们就可以直接使用,就不需要繁琐的使用$store.state.属性名来访问状态变量了
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 通过计算属性,获取状态数据 -->
          uname:{{ uname }} count:{{ count }}
        </div>
      </div>
    </template>
    
    <script>
    // mapState:映射state的辅助函数,将vuex中state的数据,映射到 computed 计算属性中
    import { mapState } from "vuex";
    
    export default {
        computed: {
            ...mapState(["uname", "count"]),
        }
    }
    </script>
    

    核心概念 - mutations

    • mutations,用来定义同步修改state状态的函数,在mutations中定义的函数,vuex调用时会传入2个参数
    • 参数一:state状态,就是vuex的state状态对象
    • 参数二:playload载荷,也就是参数,提交 mutation 时,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)

    注意:不能直接修改state中的值,例如$store.state.count++,默认情况下,vuex是允许这样修改数据的,但vuex不推荐这样做,应该遵循单向数据流,数据在vuex中,修改数据应该通过vuex的mutations或actions去修改,而不应该在子组件中直接修改,否则会导致排错困难

    如果想vuex阻止在组件中,直接修改state,可以在vuex的Vuex.Store构造函数,传入的配置对象中,配置strict属性,设置为true,就可以在直接修改时,报错

    简单使用

    • 定义mutation函数
    const store = new Vuex.Store({
        // 同步修改state的函数,要求修改state,必须通过 mutation 函数
        // 参数一:state状态,就是vuex的state状态对象
        // 参数二:playload载荷,也就是参数,提交 mutation 时,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)
        mutations: {
            // 添加数量
            addCount(state, playload) {
                state.count += playload.num
            },
            // 更新数量,为指定数量
            updateCount(state, count) {
                state.count = count
            }
        },
    });
    
    • 在按钮点击事件中,调用mutation函数
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 违反vuex的单向数据流的原则,不要这样做 -->
          <button @click="$store.state.count++">(错误)直接修改,count数据</button>
          
          <br />
          
          <!-- 修改state中的数据,要通过调用mutations中的函数来实现 -->
          <button @click="fn1">(正确)通过mutations,修改count数据</button>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
        // 触发 mutations 中的函数来修改状态
        fn1() {
          // commit(),提交函数的参数,是 mutations 中要执行的函数名
          this.$store.commit("addCount")
          
          // 传递参数,注意只能传一个参数,多个参数要用对象或数组包裹
          this.$store.commit("addCount", {
            num: 100,
            num2: 200,
          });
        },
      }
    }
    </script>
    

    优化,使用辅助映射函数

    • mapState类似,对于mutation,vuex也提供了mapMutations函数,用来映射mutations中的函数到methods方法中
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 违反vuex的单向数据流的原则,不要这样做 -->
          <button @click="$store.state.count++">(错误)直接修改,count数据</button>
          
          <br />
          
          <!-- 修改state中的数据,要通过调用mutations中的函数来实现 -->
          <button @click="fn1">(正确)通过mutations,修改count数据</button>
        </div>
      </div>
    </template>
    
    <script>
    // mapMutations:映射mutation的辅助函数,映射到 methods 中
    import { mapMutations } from "vuex";
    
    export default {
      methods: {
        // 触发 mutations 中的函数来修改状态
        fn1() {
          // commit(),提交函数的参数,是 mutations 中要执行的函数名
          // this.$store.commit("addCount")
          // 传递参数,注意只能传一个参数,多个参数要用对象或数组包裹
          // this.$store.commit("addCount", {
          //   num: 100,
          //   num2: 200,
          // });
    
          // 调用 mapMutations 映射后的函数
          this.addCount({
            num: 100,
            num2: 200,
          });
        },
        // 映射mutations中的函数,到methods中,然后就可以通过 this.addCount() 来调用 mutations 中的函数
         ...mapMutations(["addCount"]),
       }
    }
    </script>
    

    核心概念 - actions

    • actions,定义异步修改state的函数,底层也是通过提交mutation来实现修改state
    • actions中的函数,vuex调用时,都会传2个参数
      • 参数一:context上下文,能获取到store身上的哪些属性和方法
      • 参数二,参数,派发 action 时,传的参数,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)

    简单使用

    • 定义action函数
    const store = new Vuex.Store({
        actions: {
            // 异步修改数据,异步修改数据必须通过调用 mutation 函数,也就是在 mutation 上套了一层action
            // 参数一:context上下文,能获取到store身上的哪些属性和方法
            // 参数二,参数,派发 action 时,传的参数,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)
            updateAsyncCount(context, num) {
                setTimeout(() => {
                    // 修改数据,提交mutation,同步修改数据
                    context.commit("updateCount", num)
                    console.log(`异步修改数据成功,新值为${num}`);
                }, 1000);
            }
        },
    });
    
    • 在按钮点击事件中,派发action函数
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <button @click="fn2">异步,修改数据</button>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
        // 触发 mutations 中的函数来修改状态
        fn2() {
          // 异步修改数据,触发action函数
          // dispatch(),派发函数的参数,是 actions 中要执行的函数名
          // 传递参数,同样也是智能传递一个参数,多个参数要用对象或数组包裹
          this.$store.dispatch("updateAsyncCount", 888)
        },
      }
    }
    </script>
    

    优化,使用辅助映射函数

    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <button @click="fn2">异步,修改数据</button>
        </div>
      </div>
    </template>
    
    <script>
    // mapActions:映射action的辅助函数,映射到 methods 中
    import { mapActions } from "vuex";
    
    export default {
      methods: {
        fn2() {
          // 调用 mapActions 映射后的函数
          this.updateAsyncCount(888);
        },
        // 映射actions中的函数,到methods中,然后就可以通过this.updateAsyncCount() 来调用 actions 中的函数
         ...mapActions(["updateAsyncCount"]),
       }
    }
    </script>
    

    核心概念 - getters

    • 除了state之外,有时我们还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters
    • getters类似vue组件中的计算属性

    基本使用

    • 在state中,定义状态变量和getters
    • getters中,定义total函数,计算状态变量list的值,得出一个总数,作为该函数的返回值
    • 使用方式:$store.getters.total,每次都要写$store.getters前缀,太繁琐
    const store = new Vuex.Store({
        // 状态,存放数据
        state: {
            list: [1, 2, 3, 4, 5, 6]
        },
        getters: {
            // 类似计算属性,通过计算某个state,或处理多个state属性来得到一个结果
            total(state) {
                return state.list.reduce((prev, current) => {
                    return prev + current
                }, 0)
            }
        },
    });
    
    • 在页面中,使用getters
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 使用vuex的getters属性 -->
          gettes属性:{{ $store.getters.total }}
        </div>
      </div>
    </template>
    
    <script>
        export default {
          name: "App",
          created() {
            // 获取 getters 属性,类似于计算属性
            console.log(this.$store.getters.total);
        }
    </script>
    

    优化,使用辅助映射函数

    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 使用vuex的getters属性 -->
          gettes属性:{{ total }}
        </div>
      </div>
    </template>
    
    <script>
    // mapGetters:映射getters的辅助函数,映射到 computed 计算属性中
    import { mapGetters } from "vuex";
    
    export default {
      computed: {
        // 映射vuex的getters属性
        ...mapGetters(["total"]),
       }
    }
    </script>
    

    模块化

    • 由于 vuex 使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。(当项目变得越来越大的时候,Vuex会变得越来越难以维护)

    创建和配置模块

    • 单独定义一个模块user.js,同样配置state、mutations、actions、getters
    • 并通过export default暴露出该模块,准备给vuex注册模块
    • 注意:namespaced属性,必须设置,否则调用时,会报错,说找不到该模块
    // 用户模块的store
    const state = () => ({
    })
    
    const getters = {
    }
    
    // 同步操作state数据
    const mutations = {
    }
    
    // 异步操作state
    const actions = {
    }
    
    export default {
        // 开启命名空间,这样 mapState、mapMutations、mapActions等辅助函数,才能使用该模块
        namespaced: true,
        state,
        getters,
        mutations,
        actions
    }
    
    • 在vuex中,注册模块
    // Vuex相关配置,注意:Vue2的Vuex版本是3版本
    import Vue from 'vue';
    import Vuex from 'vuex';
    
    // 引入用户模块的store
    import user from '@/store/modules/user'
    
    // 注册Vuex插件
    Vue.use(Vuex);
    
    // 创建仓库,管理数据
    const store = new Vuex.Store({
        ...省略其他配置
        
        // 模块
        modules: {
          // 模块名:模块对象
          // user: user
          user
        }
    });
    
    // 默认导出
    export default store
    

    使用方式的变化

    准备数据

    // 用户模块的store
    const state = () => ({
        userInfo: {
            name: 'zs',
            age: 18
        },
        num: 100
    })
    
    const getters = {
        // 返回格式化年龄的字符串
        age(state) {
            return state.num + '岁'
        },
    }
    
    // 同步操作state数据
    const mutations = {
        // 修改 num 数据
        updateNum(state) {
            state.num++
        }
    }
    
    // 异步操作state
    const actions = {
        updateAsyncNum(context) {
            // 1秒后,提交mutation,更新state
            setTimeout(() => {
                context.commit("updateNum")
            }, 1000);
        }
    }
    
    export default {
        // 开启命名空间,这样 mapState、mapMutations、mapActions等辅助函数,才能使用该模块
        namespaced: true,
        state,
        getters,
        mutations,
        actions
    }
    

    关于state

    使用模块后,之前的使用方式都需要加上模块名

    • 普通方式获取,例如
      • 未使用模块:$store.state.属性名
      • 使用模块:$store.state.模块名.属性名
    • 使用mapState辅助映射函数
      • 未使用模块:...mapState(["属性名"])
      • 使用模块:...mapState("模块名", ["属性名"])
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 模块数据,直接获取,太繁琐 -->
          用户模块-name:{{ $store.state.user.userInfo.name }} 用户模块-age:{{
            $store.state.user.userInfo.age
          }}
          
          <br />
    
          <!-- 模块数据,使用mapState辅助函数 -->
          用户模块-name:{{ userInfo.name }} 用户模块-age:{{ userInfo.age }}
        </div>
      </div>
    </template>
    
    <script>
    // mapState:映射state的辅助函数,将vuex中state的数据,映射到 computed 计算属性中
    import { mapState } from "vuex";
    
    export default {
      created() {
        console.log(this.$store.state.user);
        console.log(this.$store.state.user.userInfo.name);
        console.log(this.$store.state.user.userInfo.age);
      },
      computed: {
        // 映射vuex的state属性
        ...mapState("user", ["userInfo", "num"]),
       }
    }
    </script>
    

    关于mutations

    使用模块后,之前的使用方式都需要加上模块名

    • 普通方式获取,例如
      • 未使用模块:this.$store.commit("函数名")
      • 使用模块:this.$store.commit("模块名/函数名")
    • 使用mapMutations辅助映射函数
      • 未使用模块:...mapMutations(["函数名"])
      • 使用模块:...mapMutations("模块名", ["函数名"])
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          user模块的mutation:<button @click="fn3">{{ num }}</button>
        </div>
      </div>
    </template>
    
    <script>
    // mapMutations:映射mutation的辅助函数,映射到 methods 中
    import { mapMutations } from "vuex";
    
    export default {
      methods: {
        fn3() {
          // 调用user模块的mutation函数,格式:模块名/函数名
          this.$store.commit("user/updateNum");
          
          // 使用mapMutations辅助函数,映射后mutation函数
          this.updateNum();
        },
        // 映射user模块的mutation函数
        ...mapMutations("user", ["updateNum"]),
      }
    }
    </script>
    

    关于actions

    使用模块后,之前的使用方式都需要加上模块名

    • 普通方式获取,例如
      • 未使用模块:this.$store.dispatch("函数名")
      • 使用模块:this.$store.dispatch("模块名/函数名")
    • 使用mapMutations辅助映射函数
      • 未使用模块:...mapActions(["函数名"])
      • 使用模块:...mapActions("模块名", ["函数名"])
    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          user模块的action:<button @click="fn4">{{ num }}</button>
        </div>
      </div>
    </template>
    
    <script>
    // mapActions:映射action的辅助函数,映射到 methods 中
    import { mapActions } from "vuex";
    
    export default {
      methods: {
        fn4() {
          // 调用user模块的action函数,格式:模块名/函数名
          this.$store.dispatch("user/updateAsyncNum")
    
          // 使用mapActions辅助函数,映射后的action函数
          this.updateAsyncNum();
        },
        // 映射user模块的action模块
        ...mapActions("user", ["updateAsyncNum"]),
      }
    }
    </script>
    

    关于getters

    使用模块后,之前的使用方式都需要加上模块名

    • 普通方式获取,例如
      • 未使用模块:$store.getters.属性名
      • 使用模块:$store.getters.[模块名/属性名]
    • 使用mapState辅助映射函数
      • 未使用模块:...mapGetters(["属性名"])
      • 使用模块:...mapGetters("模块名", ["属性名"])

    注意:getters和state虽然都是类似属性的变量,但是在加入模块后,写法是有区别的,例如

    • state是:$store.state.模块名.属性名
    • getters是:$store.getters['模块名/属性名']
    • 错误写法:$store.getters.模块名.属性名

    可以看见,getters没有像state那样,在getters下再新建一个模块名的对象,而是在属性名的基础上增加模块和斜杠。那么在JS中,就必须使用中括号来访问了,也就是$store.getters['user/age'],而不能是$store.getters.'user/age',因为这是不合法的

    <template>
      <div id="app">
        <h1>根组件</h1>
        <div>
          <!-- 直接使用user模块的getter属性,太繁琐 -->
          user模块的getters:<span>年龄 {{ $store.getters['user/age'] }}</span>
          <br>
          <!-- 使用mapGetters辅助函数,映射的getter属性 -->
          user模块的getters:<span>年龄 {{ age }}</span>
        </div>
      </div>
    </template>
    
    <script>
    // mapGetters:映射getters的辅助函数,映射到 computed 计算属性中
    import { mapGetters } from "vuex";
    
    export default {
      created() {
        // 获取user模块的getter属性
        console.log(this.$store.getters['user/age']);
      },
      computed: {
        // user模块中的getter
        ...mapGetters('user', ['age'])
      }
    }
    </script>
    

    相关文章

      网友评论

        本文标题:Vuex基本使用

        本文链接:https://www.haomeiwen.com/subject/jqasmdtx.html