十一:Vuex,getters,mapState,mapGetters,mapActions,mapMutations,多组件共享数据,vuex模块化

一.vuex

1.vuex简介

是什么?

vuex在Vue中实现集中式状态(数据)管理的一个vue插件,集中式就像一个老师给一群学生上课,状态就是数据;

vuex对vue应用中多个组件的共享数据进行集中式的管理(读、写),适用于任意组件间通信

什么时候用?

多个组件依赖于同一状态时;来自不同组件的行为需要变更同一状态(数据)

vuex工作原理:

2.Vuex使用

1.安装vuex:npm i vuex@3
注意Vue2一定要安装vuex3,如果是vue3可以直接npm i vuex安装的是vuex4

2.创建store(vuex)/index.js文件:

在此文件中引入插件并使用vuex插件,使用vuex插件必须在引入store之前,如果在main.js中引入和使用vuex的话,由于js文件里所有的import语句都会提升到最开始执行,所以会报错滴。总结:引入store必须在Vue.use(Vuex)之后

// 该文件用于创建store
// 引入vue
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
Vue.use(Vuex)
// 准备actions-用于响应组件中的动作
const actions = {}
// 准备mutations - 用于操作数据(state)
const mutations = {}
// 准备state-用于存储数据
const state = {}


// 创建暴漏store,管理actions,mutations,state
export default new Vuex.Store({
    actions,
    mutations,
    state,
})
// 暴漏store
// export default store

3.main.js中引入store

import Vue from 'vue'
import App from './App.vue'

// 引入插件
// import vueResource from 'vue-resource'

// 引入store
import store from './store/index'
Vue.config.productionTip = false

// 使用插件
// Vue.use(VueResource)


new Vue({
    el: '#app',
    render: h => h(App),
    store: store,//或者直接写store
    // 安装全局事件总线,实现list接传递数据给search
    beforeCreate() {
        Vue.prototype.$bus = this
    },
})

这样就暴漏store使vc,vm都可以用$store

3.vuex求和案例

首先将初始数据给store的state:

// 准备state-用于存储数据
const state = {
    sum: 0, //当前的和
}

然后使用插值语法将和展示在页面上:

  <h1>当前求和为:{{ $store.state.sum }}</h1>

添加加法方法:

使用dispatch将回调函数传递给actions(点菜),如果只是单纯的加减,没有奇数再加等逻辑时,也可以直接使用commit,直接传递给mutations直接进行加减; 

 methods: {
    increment() {
      this.$store.dispatch("jia", this.n);
      // 使用commit直接使vc与mutations对话,跳过actions
      // this.$store.commit("JIA", this.n);
    },

然后在actions中使用jia这个函数来接(处理顾客需求)

再使用commit传向mutations进行操作数据:相当于服务员将顾客点菜告诉厨师;

其中携带数据context相当于mini版的$Store,可以调用体内的commit,value为用户选择的加几,组件传来的数据

 // value为用户选择的加几,context为minniStore,包含commit
   jia: function (context, value) {
       console.log('action中的jia被调用了', context, value);
        context.commit('JIA', value)
        },

在mutations中使用JIA函数来接传递过来的数据(接菜单),一般是actions中的函数大写,mutations厉害好吧!

第一个参数是state对象,第二个参数是传过来的数据。实现数据相加

准备mutations - 用于操作数据(state)
const mutations = {
    JIA: function (state, value) {
        console.log('mutations的JIA被调用了', state, value);
        state.sum += value
    },

最后通过state中匹配的setter,实现响应式,把数据更新到页面(上菜)。

Store/index.js:

// 该文件用于创建store
// 引入vue
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
Vue.use(Vuex)
// 准备actions-用于响应组件中的动作
const actions = {
    // value为用户选择的加几,context为minniStore,包含commit
    jia: function (context, value) {
        console.log('action中的jia被调用了', context, value);
        context.commit('JIA', value)
    },
    jian: function (context, value) {
        console.log('action中的jian被调用了', context, value);
        context.commit('JIAN', value)
    },
    jiaOdd: function (context, value) {
        console.log('action中的jiaOdd被调用了', context, value);
        if (context.state.sum % 2) {
            context.commit('JIA', value)
        }
    },
    jiaWait: function (context, value) {
        console.log('action中的jiaWait被调用了', context, value);
        setTimeout(() => {
            context.commit('JIA', value)
        }, 500);
    }

}
// 准备mutations - 用于操作数据(state)
const mutations = {
    JIA: function (state, value) {
        console.log('mutations的JIA被调用了', state, value);
        state.sum += value
    },
    JIAN: function (state, value) {
        console.log('mutations的JIAn被调用了', state, value);
        state.sum -= value
    },

}
// 准备state-用于存储数据
const state = {
    sum: 0, //当前的和
}

// 创建暴漏store,管理actions,mutations,state
export default new Vuex.Store({
    actions,
    mutations,
    state,
})

Count.vue:

<template>
  <div>
    <h1>当前求和为:{{ $store.state.sum }}</h1>
    <!-- v-model.number加number使数字强制转换为数字型 -->
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">和为奇数时再加</button>
    <button @click="incrementWait">等一等再加</button>
  </div>
</template>

<script>
export default {
  name: "count",
  data() {
    return {
      n: 1, //获取用户选择的数字
    };
  },
  methods: {
    increment() {
      this.$store.dispatch("jia", this.n);
      // 使用commit直接使vc与mutations对话,跳过actions
      // this.$store.commit("JIA", this.n);
    },
    decrement() {
      this.$store.dispatch("jian", this.n);
      // 使用commit直接使vc与mutations对话,跳过actions
      // this.$store.commit("JIAN", this.n);
    },
    incrementOdd() {
      this.$store.dispatch("jiaOdd", this.n);
    },
    incrementWait() {
      this.$store.dispatch("jiaWait", this.n);
    },
  },
};
</script>

<style scoped>
button {
  margin-left: 5px;
}
</style>

注意点:

一般来说都会把网络请求或其他业务逻辑写到actions里面
actions里面也可以操作数据,但是如果不在mutations里操作数据,而在actions里操作数据,vuex开发者工具会失效的;


二.配置项

1.getters配置项

可以使用getters对state中的数据进行在加工,类似于Vue中的计算属性computed。

在store中配置getters:

.......
// 准备state-用于存储数据
const state = {
    sum: 0, //当前的和
}
// 准备getters-用于将state中的数据进行加工
const getters = {
    bigSum(state) {
        return state.sum * 10
    }
}
// 创建暴漏store,管理actions,mutations,state
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})

然后在组件中直接读取:$store.getters.bigSum

 <h3>当前求和放大十倍:{{ $store.getters.bigSum }}</h3>

2.mapState与mapGetters

先从vuex中导入mapState与mapGetters方法:import { mapState, mapGetters, } from "vuex";

当我们使用插值语法获得state值时,只能使用$store.state.xxx或者 $store.getters.xxx;

比如这种,就比较繁琐,

<h1>当前求和为:{{  $store.state.sum  }}</h1>
<h1>当前求和放大十倍后为:{{  $store.getters.bigSum  }}</h1>
<h1>我在{{  $store.state.school  }}学习{{  $store.state.subject  }}</h1>

这时就可以使用计算属性简化:

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

这样就可以直接使用简单插值语法:

  <h1>当前求和为:{{ sum }}</h1>
    <h3>当前求和放大十倍:{{ bigSum }}</h3>
    <h3>我在{{ school }},学习{{ subject }}</h3>

但是这样复用性较差,所以vuex提供mapState和mapGetters生成计算属性读取数据:

键对应计算属性方法名,值对应state中的数据名,如果方法名和数据名一样,就可以用数组形式简写,...为扩展运算符

 computed: {
    // 借助MapState生成计算属性,从state中读取数据(对象写法)
    // ...指将mapState对象中的数据展开显示
  ...mapState({ he: "sum", xuexiao: "school", xueke: "subject" }),
    // 当计算属性名和state中的数据名相同时,可以使用数组写法
    ...mapState(["sum", "school", "subject"]),

    // 借助MapGetters生成计算属性,从state中读取数据,对象写法
    // ...mapGetters({ bigSum: "bigSum" }),
    //借助MapGetters生成计算属性,从state中读取数据, 数组写法
    ...mapGetters(["bigSum"]),
  },

3.mapMutations与mapActions

先从vuex中导入mapMutations与mapActions方法:import { mapMutations,mapActions } from "vuex";

以往vc向actions或者mutations直接传递数据时,需要在methods中借助dispatch或者commit

比如:this.$store.commit("JIA", this.n);或者this.$store.dispatch("jia", this.n);

但现在通过mapMutations与mapActions可用其生成的对应的方法联系mutations和actions

 methods: {
    // 人工写方法
    /* increment() {
      this.$store.dispatch("jia", this.n);
      // 使用commit直接使vc与mutations对话,跳过actions
      // this.$store.commit("JIA", this.n);
    },
    decrement() {
      this.$store.dispatch("jian", this.n);
      // 使用commit直接使vc与mutations对话,跳过actions
      // this.$store.commit("JIAN", this.n);
    }, */

    // 借助mapMutation生成的对应的方法,方法中会调用commit去联系mutations(对象写法)
    ...mapMutations({ increment: "JIA", decrement: "JIAN" }),
    // 借助mapMutation生成的对应的方法,方法中会调用commit去联系mutations(数组写法)
    // ...mapMutations(['JIA','JIAN']),

    // **************************************************************
    // 人工写方法
    /*   incrementOdd() {
      this.$store.dispatch("jiaOdd", this.n);
    },
    incrementWait() {
      this.$store.dispatch("jiaWait", this.n);
    }, */

    // 借助mapActions生成的对应的方法,方法中会调用dispatch去联系actions(对象写法)
    ...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiaWait" }),
    // 借助mapActions生成的对应的方法,方法中会调用dispatch去联系actions(数组写法)
    // ...mapActions(["jiaOdd", "jiaWait"]),
  },

此外mapMutations与mapActions没有传递的参数,需要在模板绑定事件时传递参数,

 <!-- 添加n传递参数,否则会传递事件对象鼠标动作 -->
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">和为奇数时再加</button>
    <button @click="incrementWait(n)">等一等再加</button>

三.多组件共享数据

通过配置可以使各个组件互相使用,比如上面的count组件可以检测到下面组件有多少人,下面组件可以检测到上面组件的和;

person.vue:

<template>
  <div>
    <h1>人员列表</h1>
    <h3>count组件和为:{{ sum }}</h3>
    <input type="text" placeholder="请输入名字" v-model="name" />
    <button @click="add">添加</button>
    <ul>
      <li v-for="p in personList" :key="p.id">{{ p.name }}</li>
    </ul>
  </div>
</template>

<script>
import { mapState } from "vuex";
import { nanoid } from "nanoid";
export default {
  name: "Person",
  data() {
    return {
      name: "",
    };
  },
  computed: {
    personList() {
      return this.$store.state.personList;
    },
    sum() {
      return this.$store.state.sum;
    },
    // 也可以通过mapState写
    // ...mapState(["sum", "personList"]),
  },
  methods: {
    add() {
      const personObj = { id: nanoid(), name: this.name };
      this.$store.commit("ADD_PERSON", personObj);
      this.name = "";
    },
  },
};
</script>

Count.vue:

<template>
  <div>
    <h1>当前求和为:{{ sum }}</h1>
    <h3>当前求和放大十倍:{{ bigSum }}</h3>
    <h3>我在{{ school }},学习{{ subject }}</h3>
    <h3>下方组件总人数为:{{ personList.length }}</h3>
    <!-- v-model.number加number使数字强制转换为数字型 -->
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">和为奇数时再加</button>
    <button @click="incrementWait(n)">等一等再加</button>
  </div>
</template>

<script>
// 引入映射状态
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {
  name: "count",
  data() {
    return {
      n: 1, //获取用户选择的数字
    };
  },
  computed: {
    ...mapState(["sum", "school", "subject", "personList"]),
    ...mapGetters(["bigSum"]),
  },

  methods: {
    ...mapMutations({ increment: "JIA", decrement: "JIAN" }),
    ...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiaWait" }),
    
  },
  mounted() {
    const x = mapState({ he: "sum", xuexiao: "school", xueke: "subject" });
    console.log(x);
  },
};
</script>

四.vuex模块化+namespaced命名空间

因为actions里面有很多响应不同组件的不同动作,mutations里有很多操作数据的不同方法,state里有很多属于不同组件的数据,所以这时可以使用vuex模块化,使不同的数据方法等分开管理;

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

可以都写到store的index.js中,也可以分开在store中写不同的js文件,比如下面分开写count.js和person.js文件

count.js:

 注意:开启命名空间namespaced: true后,才能使countAbout和personAbout的取名被识别出来。

// 求和相关配置
export default {
    // 命名空间为true才能识别countAbout名字
    namespaced: true,
    actions: {
        jiaOdd: function (context, value) {
            console.log('action中的jiaOdd被调用了', context, value);
            if (context.state.sum % 2) {
                context.commit('JIA', value)
            }
        },
        jiaWait: function (context, value) {
            console.log('action中的jiaWait被调用了', context, value);
            setTimeout(() => {
                context.commit('JIA', value)
            }, 500);
        }
    },
    mutations: {
        JIA: function (state, value) {
            console.log('mutations的JIA被调用了', state, value);
            state.sum += value
        },
        JIAN: function (state, value) {
            console.log('mutations的JIAn被调用了', state, value);
            state.sum -= value
        },
    },
    state: {
        sum: 0, //当前的和
        school: '山河大学',
        subject: '前端',
    },
    getters: {
        bigSum(state) {
            return state.sum * 10
        }
    }
}

person.js:

// 人员管理相关配置
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('请添加姓王的人')
            }
        },
        // 联系后台服务器要名字:向后台发送axios请求
        addPersonServer(context) {
            axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
                Response => {
                    context.commit('ADD_PERSON', { id: nanoid(), name: Response.data })
                },
                error => {
                    alert(error.message)
                }
            )
        }
    },
    mutations: {
        // 添加一个人
        ADD_PERSON: function (state, value) {
            console.log('mutations的ADD_PERSON被调用了', state, value);
            state.personList.unshift(value)
        }
    },
    state: {
        personList: [
            { id: '001', name: 'huahua' }
        ]
    },
    getters: {
        firstPersonName(state) {
            return state.personList[0].name
        }
    }
}

然后需要在index.js中引入js并暴漏

// 创建暴漏store,
export default new Vuex.Store({
    modules: {
        countAbout: countOptions,
        personAbout: personOptions
    }

1.开启命名空间后,读取state数据

可以在computed中添加方法

 personList() {
      return this.$store.state.personAbout.personList;
    },

也可以在computed中通过mapState读取数据,但是要标注哪里来的数据,加个参数名;

比如sum,school是来自countAbout中的数据

...mapState("countAbout", ["sum", "school", "subject"]),
...mapState("personAbout", ["personList"]),

2.开启命名空间后,读取getters数据

首先在getters中添加数据

   getters: {
        firstPersonName(state) {
            return state.personList[0].name
        }
    }

然后可以在computed中添加方法获取,此处添加参数名可以使用中括号;

  firstPersonName() {
      return this.$store.getters["personAbout / firstPersonName"];
    },

也可以使用mapGetters读取:

 ...mapGetters("countAbout", ["bigSum"]),

3.开启命名空间后,调用dispatch

可以直接在mathods中通过dispatch调用personAbout中的actions里的方法:

   addPersonServer() {
      this.$store.dispatch("personAbout/addPersonServer");
    },

也可以在methods中通过mapActions调用countAbout中actions里的方法:

...mapActions("countAbout", {incrementOdd: "jiaOdd",incrementWait: "jiaWait",}),

4.开启命名空间后,调用commit

可以在methods的方法中直接使用commit,调用 personAbout里的mutations里的方法:

  methods: {
    add() {
      const personObj = { id: nanoid(), name: this.name };
      this.$store.commit("personAbout/ADD_PERSON", personObj);
      this.name = "";
    },

也可以直接使用mapMutations,

...mapMutations("countAbout", { increment: "JIA", decrement: "JIAN" }),


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值