Vue —— 进阶 Vuex(一)(四个 map 的用法、模块化和命名空间)

简介: Vue —— 进阶 Vuex(一)(四个 map 的用法、模块化和命名空间)

一、四个 map 的用法

1. mapState 方法

用于帮助我们映射 state 中的数据为计算属性

  computed: {
    // 借助 mapState 生成计算属性:sum、school、subject (对象写法)
    ...mapState({ sum: "sum", school: "school", subject: "subject" })
    // 借助 mapState 生成计算属性:sum、school、subject (数组写法)
    ...mapState(['sum', 'school', 'subject'])
  }
2. mapGetters 方法

用于帮助我们映射 getters 中的数据为计算属性

  computed:{
    // 借助 mapGetters 生成计算属性:bigSum (对象写法)
    ...mapGetters({bigSum: bigSum})
    // 借助 mapGetters 生成计算属性:bigSum (数组写法)
    ...mapGetters(['bigSum'])
  }
实例:使用 mapState 和 mapGetters

使用 mapState 和 mapGetters,可以少些很多代码。

index.js

  // 该文件用于创建Vuex中最为核心的store
  // 引入vue核心库
  import Vue from 'vue'
  // 引入Vuex
  import Vuex from 'vuex'
  // 应用Vuex插件
  Vue.use(Vuex)
  // 准备actions——用于响应组件中的动作
  const actions = {
      addOdd(context, value){
          console.log('actions中的addOdd被调用了');
          if(context.state.sum % 2){
              context.commit('ADD',value)
          }
      },
      addWait(context, value){
          console.log('actions中的addWait被调用了');
          setTimeout(()=>{
              context.commit('ADD', value)
          },500)
      }
  }
  // 准备mutations——用于修改state中的数据(state)
  const mutations = {
      ADD(state, value){
          console.log('mutations中的ADD被调用了',state, value);
          state.sum += value
      },
      SUB(state, value){
          console.log('mutations中的SUB被调用了',state, value);
          state.sum -= value
      }
  }
  // 准备state——用于存储具体的数据
  const state = {
      sum: 0, //当前的和
      school: '尚硅谷',
      subject: '前端'
  } 
  // 准备getters——用于将state中的数据进行加工
  const getters = {
      bigSum(state){
          return state.sum * 10
      }
  }
  // 创建并暴露store
  export default new Vuex({
      actions,
      mutations,
      state,
      getters
  })

Count.vue

  1. 要在该组件导入 mapState 和 mapGetters
  2. import { mapState, mapGetters } from "vuex";
  <template>
    <div>
      <h2>当前求和为:{{ sum }}</h2>
      <h3>当前求和放大10倍:{{ bigSum }}</h3>
      <h3>我在{{ school }},学习{{ subject }}</h3>
      <select v-model.number="n">
        <option :value="1">1</option>
        <option :value="2">2</option>
        <option :value="3">3</option>
      </select>
      <button @click="add(n)">+</button>
      <button @click="sub(n)">-</button>
      <button @click="addOdd(n)">当前求和为奇数再加</button>
      <button @click="addWait(n)">等一等再加</button>
    </div>
  </template>
  <script>
  import { mapState, mapGetters } from "vuex";
  export default {
    name: "myCount",
    data() {
      return {
        n: 1, //用户选择的数字
      };
    },
    computed: {
      // 借助mapState生成计算属性,从state中读取数据 (对象写法)
      //...mapState({ sum: "sum", school: "school", subject: "subject" }),
      // 借助mapState生成计算属性,从state中读取数据 (数组写法)
      ...mapState(['sum','school','subject']),
      // 借助mapGetters生成计算属性,从getters中读取数据(对象写法)
      //...mapGetters({bigSum: 'bigSum'}),
      // 借助mapGetters生成计算属性,从getters中读取数据(数组写法)
      ...mapGetters(['bigSum'])
    },
    methods: {
      increment() {
        this.$store.commit("ADD", this.n);
      },
      decrement() {
        this.$store.cmommit("SUB", this.n);
      },
      incrementOdd() {
        this.$store.dispatch("addOdd", this.n);
      },
      incrementWait() {
        this.$store.dispatch("addWait", this.n);
      },
    },
  };
  </script>
  <style scoped>
  button {
    margin-left: 5px;
  }
  </style>

629d0afd80e049dab400bd8108e1030b.png

如果不使用 mapState 和 mapGetters,则需要在 computed 中手动配置如下代码。

  computed: {
    sum() {
      return this.$store.state.sum;
    },
    school() {
      return this.$store.state.school;
    },
    subject() {
      return this.$store.state.subject;
    },
    bigSum() {
      return this.$store.getters.bigSum;
    }
  }

629d0afd80e049dab400bd8108e1030b.png

3. mapActions 方法

用于帮助我们生成与 actions 对话的方法,即:包含 $store.dispatch(xxx) 的函数。

  methods: {
    // 靠mapActions生成:addOdd、addWait (对象写法)
    ...mapActions({addOdd: 'addOdd', addWait: 'addWait'})
    // 靠mapActions生成:addOdd、addWait (数组写法)
    ...mapActions(['addOdd', 'addWait'])
  }
4. mapMutations 方法

用于帮助我们生成与 mutations 对话的方法,即:包含 $store.commit(xxx) 的函数。

  methods: {
      // 靠mapMutations生成:ADD、SUB (对象写法)
      ...mapMutations({add: 'ADD', sub: 'SUB'})
      // 靠mapMutations生成:ADD、SUB (数组写法)
      ...mapMutations(['ADD', 'SUB'])
  }
实例:使用 mapActions 和 mapMutations

index.js 不变


Count.vue


要在该组件导入 mapActions 和 mapMutations

import { mapActions, mapMutations} from "vuex";

  <template>
    <div>
      <h2>当前求和为:{{ sum }}</h2>
      <h3>当前求和放大10倍:{{ bigSum }}</h3>
      <h3>我在{{ school }},学习{{ subject }}</h3>
      <select v-model.number="n">
        <option :value="1">1</option>
        <option :value="2">2</option>
        <option :value="3">3</option>
      </select>
      <button @click="add(n)">+</button>
      <button @click="sub(n)">-</button>
      <button @click="addOdd(n)">当前求和为奇数再加</button>
      <button @click="addWait(n)">等一等再加</button>
    </div>
  </template>
  <script>
  import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
  export default {
    name: "myCount",
    data() {
      return {
        n: 1, //用户选择的数字
      };
    },
    computed: {
      // 借助mapState生成计算属性,从state中读取数据 (对象写法)
      // ...mapState({ sum: "sum", school: "school", subject: "subject" }),
      // 借助mapState生成计算属性,从state中读取数据 (数组写法)
      ...mapState(["sum", "school", "subject"]),
      // 借助mapGetters生成计算属性,从getters中读取数据(对象写法)
      //...mapGetters({bigSum: 'bigSum'}),
      // 借助mapGetters生成计算属性,从getters中读取数据(数组写法)
      ...mapGetters(["bigSum"]),
    },
    methods: {
      //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations (对象的写法)
      ...mapMutations({add: 'ADD', sub: 'SUB'}),
      //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations (数组的写法)
      //...mapMutations(['ADD','SUB']), //此时上面的绑定也要 @ADD @SUB
      //借助mapActions生成对应的方法,方法中会调用dispatch去联系actions (对象写法)
      ...mapActions({addOdd: 'addOdd', addWait: 'addWait'})
      //借助mapActions生成对应的方法,方法中会调用dispatch去联系actions (数组写法)
      //...mapActions(['addOdd','addWait'])
    },
  };
  </script>
  <style scoped>
  button {
    margin-left: 5px;
  }
  </style>

二、多组件共享数据

基本功能

  1. Person 组件添加一个人,Count 组件响应地加一
  2. Count 组件加一,Person 组件响应地加一

https://www.bilibili.com/video/BV1r3411G7KS?t=15.8

多组件共享

index.js

  // 该文件用于创建Vuex中最为核心的store
  // 引入vue核心库
  import Vue from 'vue'
  // 引入Vuex
  import Vuex from 'vuex'
  // 应用Vuex插件
  Vue.use(Vuex)
  // 准备actions——用于响应组件中的动作
  const actions = {
      addOdd(context, value) {
          console.log('actions中的addOdd被调用了');
          if (context.state.sum % 2) {
              context.commit('ADD', value)
          }
      },
      addWait(context, value) {
          console.log('actions中的addWait被调用了');
          setTimeout(() => {
              context.commit('ADD', value)
          }, 500)
      }
  }
  // 准备mutations——用于修改state中的数据(state)
  const mutations = {
      ADD(state, value) {
          console.log('mutations中的ADD被调用了');
          state.sum += value
      },
      SUB(state, value) {
          console.log('mutations中的SUB被调用了');
          state.sum -= value
      },
      ADD_PERSON(state, value) {
          console.log('mutations中的ADD_PERSON被调用了');
          state.personList.unshift(value)
      }
  }
  // 准备state——用于存储具体的数据
  const state = {
      sum: 0, //当前的和
      school: '清华大学',
      subject: 'Web前端',
      personList: [{
          id: '001',
          name: '张三'
      }]
  }
  const getters = {
      bigSum() {
          return state.sum * 10
      }
  }
  // 创建并暴露store
  export default new Vuex.Store({
      actions,
      mutations,
      state,
      getters
  })

Count.vue

  1. 使用 map 写法
  <template>
    <div>
      <h2>当前求和为:{{ sum }}</h2>
      <h3>当前求和放大10倍:{{ bigSum }}</h3>
      <h3>我在{{ school }},学习{{ subject }}</h3>
      <h3 style="color: red">Person组件的总人数是:{{ personList.length }}</h3>
      <select v-model.number="n">
        <option :value="1">1</option>
        <option :value="2">2</option>
        <option :value="3">3</option>
      </select>
      <button @click="add(n)">+</button>
      <button @click="sub(n)">-</button>
      <button @click="addOdd(n)">当前求和为奇数再加</button>
      <button @click="addWait(n)">等一等再加</button>
    </div>
  </template>
  <script>
  import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
  export default {
    name: "myCount",
    data() {
      return {
        n: 1, //用户选择的数字
      };
    },
    computed: {
      // 借助mapState生成计算属性,从state中读取数据 (对象写法)
      // ...mapState({ sum: "sum", school: "school", subject: "subject" }),
      // 借助mapState生成计算属性,从state中读取数据 (数组写法)
      ...mapState(["sum", "school", "subject", "personList"]),
      // 借助mapGetters生成计算属性,从getters中读取数据(对象写法)
      //...mapGetters({bigSum: 'bigSum'}),
      // 借助mapGetters生成计算属性,从getters中读取数据(数组写法)
      ...mapGetters(["bigSum"]),
    },
    methods: {
      //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations (对象的写法)
      ...mapMutations({ add: "ADD", sub: "SUB" }),
      //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations (数组的写法)
      // ...mapMutations(['ADD','SUB']), //此时上面的绑定也要 @ADD @SUB
      ...mapActions({ addOdd: "addOdd", addWait: "addWait" }),
      // ...mapActions(['addOdd','addWait'])
    },
  };
  </script>
  <style scoped>
  button {
    margin-left: 5px;
  }
  </style>

Person.vue

  1. 使用普通的写法
  <template>
    <div>
      <h3>人员列表</h3>
      <h3 style="color: red">Count组件的求和为:{{ sum }}</h3>
      <input type="text" placeholder="请输入名字" v-model="name" />
      <button @click="addPerson">添加</button>
      <ul>
        <li v-for="p in personList" :key="p.id">{{ p.name }}</li>
      </ul>
    </div>
  </template>
  <script>
  import { nanoid } from "nanoid";
  export default {
    name: "myPerson",
    data() {
      return {
        name: "",
      };
    },
    computed: {
      personList() {
        return this.$store.state.personList;
      },
      sum() {
        return this.$store.state.sum;
      },
    },
    methods: {
      addPerson() {
        const personObj = { id: nanoid(), name: this.name };
        this.$store.commit("ADD_PERSON", personObj);
        this.name = "";
      },
    },
  };
  </script>

三、模块化(module)和命名空间(namespace)

1. 目的

让代码更好维护,让多种数据分类更加明确。

2. 修改 .store/xxx.js 文件

index.js

  1. 使用模块化编程
  2. 导入 count.js 和 person.js 文件
  3. 在 modules 中配置 countOptions 和 personAbout
  // 引入vue核心库
  import Vue from 'vue'
  // 引入Vuex
  import Vuex from 'vuex'
  import countOptions from './count'
  import personOptions from './person'
  // 应用Vuex插件
  Vue.use(Vuex)
  // 创建并暴露store
  export default new Vuex.Store({
      modules: {
          countAbout: countOptions,
          personAbout: personOptions
      }
  })

count.js

  1. 此文件内容为求和相关的配置
  2. 注意开启命名空间,用于解决不同模块的命名冲突问题。
  const countAbout = {
      namespaced: true, //开启命名空间
      actions: {...},
      mutations: {...},
      state: {...},
      getters: {
          xxx(state){
              return state.x
          }
      }
  }

person.js

  1. 此文件内容为人员信息相关的配置
  2. 注意开启命名空间,用于解决不同模块的命名冲突问题。
  const personAbout = {
      namespaced: true, //开启命名空间
      actions: {},
      mutations: {},
      state: {},
      getters: {}
  }
3. 组件中读取 state 数据

开启命名空间后,组件中读取 state 数据

  //方式一:自己读取数据
  this.$store.state.personAbout.personlist
  //方式二:借助 mapState 读取
  ...mapState('countAbout', ['sum', 'school', 'subject'])
4. 组件中读取 getters 数据

开启命名空间后,组件中读取 getters 数据

  //方式一:自己直接读取
  this.$store.getters['personAbout/firstPersonName']
  //方式二:借助 mapGetters 读取
  ...mapGetters('countAbout', ['bigSum'])
5. 组件中调用 dispatch

开启命名空间后,组件中调用 dispatch

  //方式一:自己直接调用 dispatch
  this.$store.dispatch('personAbout/addPersonWang', person)
  //方式二:借助 mapActions 调用
  ...mapActions('countAbout', {addOdd: 'addOdd', addWait: 'addWait'})
6. 组件中调用 commit

开启命名空间后,组件中调用 commit

  //方式一:自己直接调用 commit
  this.$store.commit('personAbout/ADD_PERSON', person)
  //方式二:借助 mapMutations 调用
  ...mapMutations('countAbout', {add: 'ADD', sub: 'SUB'})

四、模块化实例

效果如下:

https://www.bilibili.com/video/BV1G3411V73h?t=1.2

vuex模块化

index.js

  1. 模块化 count.js 和 person.js
  // 引入vue核心库
  import Vue from 'vue'
  // 引入Vuex
  import Vuex from 'vuex'
  import countOptions from './count'
  import personOptions from './person'
  // 应用Vuex插件
  Vue.use(Vuex)
  // 创建并暴露store
  export default new Vuex.Store({
      modules: {
          countAbout: countOptions,
          personAbout: personOptions
      }
  })

count.js

  1. 求和相关的配置
  // 求和相关的配置
  export default {
    // 开启命名空间
    namespaced: true,
    // 用于响应组件中的动作
    actions: {
      addOdd(context, value) {
        console.log('actions中的addOdd被调用了');
        if (context.state.sum % 2) {
          context.commit('ADD', value)
        }
      },
      addWait(context, value) {
        console.log('actions中的addWait被调用了');
          setTimeout(() => {
            context.commit('ADD', value)
          }, 500)
       }
    },
    // 用于修改state中的数据(state)
    mutations: {
      ADD(state, value) {
        console.log('mutations中的ADD被调用了');
        state.sum += value
      },
      SUB(state, value) {
        console.log('mutations中的SUB被调用了');
        state.sum -= value
      },
    },
    // 用于存储具体的数据
    state: {
      sum: 0, //当前的和
      school: '清华大学',
      subject: 'Web前端',
    },
    getters: {
      bigSum(state) {
        return state.sum * 10
      }
      }
  }

person.js

  1. 人员相关的配置
  import axios from 'axios'
  import { nanoid } from 'nanoid'
  export default {
    // 开启命名空间
    namespaced: true,
    // 用于响应组件中的动作
    actions: {
      addPersonWang(context, value){
        if(value.name.indexOf('王') === 0){
          context.commit('ADD_PERSON', value)
        }else{
          alert('添加的人必须姓王!')
          }
        },
      //连接服务器
      addPersonServer(context){
        axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
          response => {
            context.commit('ADD_PERSON', {id:nanoid(), name:response.data})
          },
          error => {
            alert(error.message)
          }
        )
      }
      },
    // 用于修改state中的数据(state)
    mutations: {
      ADD_PERSON(state, value) {
        console.log('mutations中的ADD_PERSON被调用了');
        state.personList.unshift(value)
      }
    },
    // 用于存储具体的数据
    state: {
      personList: [{
        id: '001',
        name: '张三'
      }]
    },
    getters: {
      firstPersonName(state){
        return state.personList[0].name
      }
    }
    }

Count.vue

  <template>
    <div>
      <h2>当前求和为:{{ sum }}</h2>
      <h3>当前求和放大10倍:{{ bigSum }}</h3>
      <h3>我在{{ school }},学习{{ subject }}</h3>
      <h3 style="color: red">Person组件的总人数是:{{ personList.length }}</h3>
      <select v-model.number="n">
        <option :value="1">1</option>
        <option :value="2">2</option>
        <option :value="3">3</option>
      </select>
      <button @click="add(n)">+</button>
      <button @click="sub(n)">-</button>
      <button @click="addOdd(n)">当前求和为奇数再加</button>
      <button @click="addWait(n)">等一等再加</button>
    </div>
  </template>
  <script>
  import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
  export default {
    name: "myCount",
    data() {
      return {
        n: 1, //用户选择的数字
      };
    },
    computed: {
      ...mapState("countAbout", ['sum','school','subject']),
      ...mapState("personAbout", ['personList']),
      ...mapGetters('countAbout', ['bigSum']),
    },
    methods: {
      ...mapMutations('countAbout', { add: "ADD", sub: "SUB" }),
      ...mapActions('countAbout', { addOdd: "addOdd", addWait: "addWait" }),
    },
  };
  </script>
  <style scoped>
  button {
    margin-left: 5px;
  }
  </style>

Person.vue

  <template>
    <div>
      <h3>人员列表</h3>
      <h3 style="color: red">Count组件的求和为:{{ sum }}</h3>
      <h3>列表中第一个人的名字是:{{firstPersonName}}</h3>
      <input type="text" placeholder="请输入名字" v-model="name" />
      <button @click="addPerson">添加</button>
      <button @click="addPersonWang">添加姓王的</button>
      <button @click="addPersonServer">添加随机人</button>
      <ul>
        <li v-for="p in personList" :key="p.id">{{ p.name }}</li>
      </ul>
    </div>
  </template>
  <script>
  import { nanoid } from "nanoid";
  export default {
    name: "myPerson",
    data() {
      return {
        name: "",
      };
    }, 
    computed: {
      personList() {
        return this.$store.state.personAbout.personList;
      },
      sum() {
        return this.$store.state.countAbout.sum;
      },
      firstPersonName(){
          return this.$store.getters['personAbout/firstPersonName']
      }
    },
    methods: {
      addPerson() {
        const personObj = { id: nanoid(), name: this.name };
        this.$store.commit("personAbout/ADD_PERSON", personObj);
        this.name = "";
      },
      addPersonWang(){
          const personObj = { id: nanoid(), name: this.name };
          this.$store.dispatch('personAbout/addPersonWang', personObj)
          this.name = ''
      },
      addPersonServer(){
        this.$store.dispatch('personAbout/addPersonServer')
      }
    },
  };
  </script>
  <style scoped>
  button{
    margin-left: 5px;
  }
  </style>

不积跬步无以至千里 不积小流无以成江海

相关文章
|
2天前
|
资源调度 JavaScript 前端开发
【vue】vue中的路由vue-router,vue-cli脚手架详细使用教程
【vue】vue中的路由vue-router,vue-cli脚手架详细使用教程
|
2天前
|
JavaScript 前端开发
vue组件化开发流程梳理,拿来即用
vue组件化开发流程梳理,拿来即用
|
2天前
|
JavaScript
Vue 中如何模块化使用 Vuex
Vue 中如何模块化使用 Vuex
|
2天前
|
JavaScript Go
Vue路由跳转及路由传参
Vue路由跳转及路由传参
|
3天前
|
JavaScript 前端开发 BI
采用前后端分离Vue,Ant-Design技术开发的(手麻系统成品源码)适用于三甲医院
开发环境 技术架构:前后端分离 开发语言:C#.net6.0 开发工具:vs2022,vscode 前端框架:Vue,Ant-Design 后端框架:百小僧开源框架 数 据 库:sqlserver2019
采用前后端分离Vue,Ant-Design技术开发的(手麻系统成品源码)适用于三甲医院
|
6天前
|
JavaScript
【vue】如何跳转路由到指定页面位置
【vue】如何跳转路由到指定页面位置
10 0
|
6天前
|
资源调度 JavaScript 前端开发
【vue】vue-cli版本选择和比较(vue-cli3.0新版本如何创建项目)
【vue】vue-cli版本选择和比较(vue-cli3.0新版本如何创建项目)
14 1
|
6天前
|
JavaScript 前端开发
【vue】iview如何把input输入框和点击输入框之后的边框去掉
【vue】iview如何把input输入框和点击输入框之后的边框去掉
12 0
|
6天前
|
JavaScript UED
【vue】iview组件 DatePicker 日期选择器如何显示默认当前日期
【vue】iview组件 DatePicker 日期选择器如何显示默认当前日期
10 1
|
2天前
|
JavaScript
【vue】深入探讨vue中组件间多种传值方式
【vue】深入探讨vue中组件间多种传值方式
【vue】深入探讨vue中组件间多种传值方式