Vue2(十二):Vuex环境搭建、Vuex工作原理、Vuex开发者工具、几个配置项、多组件共享数据、Vuex模块化

一、Vuex

1.概念

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

多组件比如a、b、c、d进行事件共享的时候,都想要a里的数据,而且他们互为兄弟,按以前的方法就得用全局事件总线,但是那样的话组件就太多了。Vuex就专门解决共享数据的问题。单独放在一块区域,大家都想得到的数据就放它里面,大家都可以读、写,a改完之后的x=4,那么b在拿到x也是4。

2.什么时候使用Vuex

(1)多个组件依赖于同一状态
(2)来自不同组件的行为需要变更同一状态

3.求和案例:纯Vue版

<template>
 <div>
   <h2>当前求和为:{{sum}}</h2>
   <select v-model.number="n">
     <option value="1">1</option>
     <option value="2">2</option>
     <option value="3">3</option>
     <!-- 这里除了最开始设置的数字1,其他都是字符串所以都加不了了 -->
     <!-- 所以加 :value,当成js表达式去解析 或者加.number强制转换-->
   </select>
   <button @click="increment">+</button>
   <button @click="decrement">-</button>
   <button @click="incrementOdd">当前求和为奇数再加</button>
   <button @click="incrementWait">等一等再加</button>
 </div>
</template>

<script>
export default {
  name: "myCount",
  data(){
    return {
      sum:0,
      n:1,//用户选择的数字
    }
  },
  methods:{
    increment(){
      this.sum+=this.n
    },
    decrement(){
      this.sum-=this.n
    },
    incrementOdd(){
      if(this.sum%2==1)
      {
        this.sum+=this.n
      }
    },
    incrementWait(){
      setTimeout(()=>{
        this.sum+=this.n
      },500)
    }
  }
};
</script>

4.Vuex的工作原理图

(1)构成

构成Vuex的三个对象由store管理,而且这三个对象数据类型都是obj,dispatch、commit函数就是store里的,所以我们得让任何vc都能看见store。

(2)流程

Vue Components是组件们,比如说我要加2,然后这个数据传给dispatch函数,传参过去:第一个参数:你要做的动作,第二个参数:数字。

然后你写的函数在Actions(数据类型是Object)就会有一个函数跟它对应,然后你自己再去调用commit函数(提交),到了mutations(数据类型也是Object),commit里的jia,mutations也会有一个jia跟它对应,同时它还会拿到两个参数:state状态和2。

mutate不用你调用,只需要在mutations里的jia写一句state.sum+=2,底层自动加2,sum就不是0是2了,然后Vuex帮你开始渲染render,页面上的sum就变化了。

这样看起来好像Actions有点没用,但是上面是后端接口,因为有的时候给dispatch传只传了动作没有值,就得去后端问一下数据(值得要发送ajax请求才能得到的时候,就需要用到Actions了)。

如果传过来就有值的话,可以直接调用commit。

二、Vuex环境搭建

1.安装Vuex

npm i vuex@3,Vue2对应vuex3版本,Vue3对应vuex4版本

2.引入并Use一下vuex

import Vuex from 'Vuex'
Vue.use(Vuex)

use了vuex然后就可以在vm中创建store对象了

3.创建store

新建一个store文件夹,在里面新建一个index.js

注意:所有的import都是先被提到代码最上方先执行然后再执行其他代码

在main.js引入插件并use vuex插件必须在import store之前,所以在main里面不管把use怎么移动都是先import store会报错,干脆把use代码写在index.js里,index里没有vue就引入vue

main.js:

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
import store from './store/index.js'
//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus=this
	},
	store
})

index.js:

//该文件用于创建Vuex中最核心的store
//actions——响应组件中的动作
//引入Vue
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions={

}
//mutations——用于操作数据
const mutations={

}
//state——用于存储数据
const state={

}
//创建store还得向外暴露
const store= new Vuex.Store({
actions,
mutations,
state
})

export default  store

4.求和案例:Vuex版本

(1)首先把sum放进vuex

//state——用于存储数据
const state = {
    sum: 0,
}

(2)插值语法

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

(3)在组件中的回调用dispatch发给actions

methods:{
    increment(){
      this.$store.dispatch('jia',this.n)
      // $store是在vc身上的
    },

(4)actions来接数据,再调用commit函数

const actions = {
    jia(context,value) {
        context.commit('JIA',value)
        //context就是一个mini版的store
        // 调用commit函数,传过去方法和值
    }
}

(5)mutations来接数据

我们不是说actions里的东西要想用就得保证mutations里面也得有吗,所以写完actions里的东西之后在mutations里也加上,为了区分mutations里面都用大写

//mutations——用于操作数据
const mutations = {
    JIA(state,value) {
        state.sum+=value
    }
}

我们写的state里面只有一个sum=0,但是如果输出一下就能发现实际上还有getter和setter之类的,是vue给我们封装的,类似data

基础版index:
const actions = {
    // jia(context,value) {
    //     context.commit('JIA',value)
    //     //context就是一个mini版的store
    //     // 调用commit函数,传过去方法和值
    // },
    // jian(context,value) {
    //     context.commit('JIAN',value)
    // },

    //这两个都没必要绕弯再去找mutations,直接去找
    jiaOdd(context,value) {
        if(context.state.sum%2)
        {
            context.commit('JIA',value)
            //这儿不用写JIAODD因为调用的都是jia,然后下面也就不用加JIAODD了
        }
    },
    jiaWait(context,value) {
        setTimeout(()=>{
            context.commit('JIA',value)
        },500)
    },
}
//mutations——用于操作数据
const mutations = {
    JIA(state,value) {
        state.sum+=value
    },
    JIAN(state,value) {
        state.sum-=value
    }
}
基础版myCount:
methods: {
    increment() {
      //this.$store.dispatch("jia", this.n);
      this.$store.commit("JIA", this.n);
      // $store是在vc身上的
    },
    decrement() {
      //this.$store.dispatch("jian", this.n);
      this.$store.commit("JIAN", this.n);
      //直接去找mutations
    },
    incrementOdd() {
      this.$store.dispatch("jiaOdd", this.n);
    },
    incrementWait() {
      this.$store.dispatch("jiaWait", this.n);
    },
  },
};

注意点:1.JIAODD、JIAWAIT里的方法都是JIA不用再单独写一个

2.JIA、JIAN都可以省略找Actions,直接去找Mutations

3.如果在actions直接写context.state.num+=value也能奏效,不用再找Mutations,但是!!这样开发者工具就失效了,所以还是得按照标准写。

4.业务逻辑写在组件里不写在action行不行?拿发票报销举例子,在组件里写就是调用第一个地儿然后传单号,然后调用第、、、个地儿再传单号很麻烦,直接告诉actions我要报销然后传单号,剩下的事让actions去解决。

三、Vuex开发者工具

跟vue位置一样,像表一样的图案就是Vuex的开发者工具,每一栏操作的后面有三个按钮,第一个按钮下载一样的是点击哪个,这个和它所有之前的都合并作为基底

第二个按钮是取消某一层,而且取消之后它后面的那些层也就都没了,就像盖楼一样,三层塌了上面的都得没

第三个按钮是时光穿梭到某个时候,展示那个时候的数据

哪条最绿说明页面正在呈现哪层

展示栏的右上角是导出和导入操作步骤

四、配置项

1.getters配置项

1、概念:当state中的数据需要经过加工后再使用时,可以使用getters加工,类似Vue中的计算属性computed与data。
2、类似于计算属性,但是好多组件都可以用,computer属性只能当前属性用(逻辑复杂或者逻辑还想复用的时候就用getters,得写返回值

//用于将state的数据进行加工
const getters={
    bigSum(state){
        return state.sum*10
    }
}

配置完记得暴露,调用一下:

<h2>当前和乘十为:{{ $store.getters.bigSum }}</h2>

2.mapstate与mapGetters

(1)mapstate

当我用插值语法用index中state中的数据的时候,都还得写$store.state、、、,写多了很麻烦,vuex为我们准备了一个方法:

首先先引入:

import {mapState} from 'vuex'
computed: {
    ...mapState({ sum: "sum", school: "school", subject: "subject" }),
  },

...是es6语法,因为mapState也是一个对象,不能{  }里面再直接套一个对象{},又不是插值语法,...最后再加逗号,里面第一个是上面div要用的,第二个是index里的命名,自动就给你补齐$store.state了,然后在div里直接用:

 <h2>我在{{ school }}里学习{{ subject }}</h2>

a:a才能简写为a,但是num:‘num’不能简写(对象写法不能简写)

但是数组方法可以简写:注意:这是前后两个名字相同的情况下!!

一个名字两个用途,既可以用在index,也可以用在myCount组件

...mapState(['sum','school', 'subject' ]),

(2)mapGetters

用法一样

...mapGetters(['bigSum ' ]),
//...mapGetters({bigSum='bigSum'}),

3.mapActions与mapMutations

(1)mapMutations

借助mapMutations生成对应的方法,方法中会调用commit去联系Mutations

methods: {
    // increment() {
    //   //this.$store.dispatch("jia", this.n);
    //   this.$store.commit("JIA", this.n);
    //   // $store是在vc身上的
    // },
    // decrement() {
    //   //this.$store.dispatch("jian", this.n);
    //   this.$store.commit("JIAN", this.n);
    //   //直接去找mutations
    // },
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),

但是这么写还有点问题,确实被调用了,但是没有告诉人家n是多少,人家就默认传过去的value是鼠标点击事件,所以调用函数的时候得把n传过去

    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>

(2)mapActions

借助mapActions生成对应的方法,方法中会调用dispatch去联系Actions

 // incrementOdd() {
    //   this.$store.dispatch("jiaOdd", this.n);
    // },
    // incrementWait() {
    //   this.$store.dispatch("jiaWait", this.n);
    // },
    ...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiaWait" }),

五、多组件共享数据

mapstate引入state里其他组件的数据,然后直接插值语法用就行

computed: {
      ...mapState(['personList','sum'])
  },
<h2>上方组件的求和为:{{sum}}</h2>

六、Vuex模块化+命名空间

目的

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

(1)Count组件

我们的action、mutations、state包含了两个组件的内容,如果内容很多的话,写一块就很乱,可以把他们分开写

//求和相关的配置
const countOptions={
    actions:{
        jiaOdd(context,value) {
            if(context.state.sum%2)
            {
                context.commit('JIA',value)
                //这儿不用写JIAODD因为调用的都是jia,然后下面也就不用加JIAODD了
            }
        },
        jiaWait(context,value) {
            setTimeout(()=>{
                context.commit('JIA',value)
            },500)
        },
    },
    mutations:{
        JIA(state,value) {
            state.sum+=value
        },
        JIAN(state,value) {
            state.sum-=value
        },
    },
    state:{
        sum: 0,
        school:'bj',
        subject:'qd',
    },
    getters:{
        bigSum(state){
            return state.sum*10
        }
    },
}
//人员相关的配置
const personOptions={
    actions:{},
    mutations:{
        ADD_PERSON(state,value){
            //value就是人的对象obj 
            state.personList.unshift(value)
            //往前放
        }
    },
    state:{
        personList:[
            {id:'001',name:'tt'}
        ]
    },
    getters:{},
}
const store = new Vuex.Store({
    modules:{
        a:countOptions,
        b:personOptions
        //此时store里面就剩a、b了,mapState下的sum根本找不着了
    }
})

再想在组件里用可不能直接写num、school啥的了,因为store里只有a和b,就得带着a、b引用

computed: {
    //...mapState({ sum: "sum", school: "school", subject: "subject" }),
    ...mapState(['a', 'b']),
<h2>当前求和为:{{ a.sum }}</h2>
    <h2>当前和乘十为:{{ bigSum }}</h2>
    <h2>我在{{ a.school }}里学习{{ a.subject }}</h2>
    <h3>下方组件的总人数是:{{b.personList.length}}</h3>

哪儿都带着a、b调用吧又很麻烦,可以直接在computed里面给这些东西加a. b.

computed: {
    ...mapState('a',['sum','school','subject']), 
    ...mapState('b',['personList']), 

但是这个用法得配合开启命名空间namespaced:true,用,否则不生效,自己定义的配置后面都得写

const personOptions={
    namespaced:true,

包括方法啥的也得改是谁谁下的,后面是数组是对象都行

methods: {
    ...mapMutations('a',{ increment: "JIA", decrement: "JIAN" }),
    ...mapActions('a',{ incrementOdd: "jiaOdd", incrementWait: "jiaWait" }),
  },

(2)person组件

前面提到的要改正的不再多说,person要想调用ADD_PERSON方法,在前面加???/

this.$store.commit('b/ADD_PERSON',personObj)

又添加了一些功能并且把两个js分出去单独写了

index.js
//该文件用于创建Vuex中最核心的store
//actions——响应组件中的动作
//引入Vue
import Vue from 'vue'
import Vuex from 'vuex'
import personOptions from './myPerson'
import countOptions from './myCount'
Vue.use(Vuex)

 
//创建store还得向外暴露
const store = new Vuex.Store({
    modules:{
        a:countOptions,
        b:personOptions
        //此时store里面就剩a、b了,mapState下的sum根本找不着了
    }
})

export default store
myPerson.vue
<template>
  <div>
    <h1>人员列表</h1>
    <h3>我想返回的列表的第一个人的名字是:{{ firstPersonName }}</h3>
    <h2>上方组件的求和为:{{ a.sum }}</h2>
    <input type="text" placeholder="请输入姓名" v-model="name" />
    <button @click="add">添加</button>
    <button @click="addWang">添加一个姓王的人</button>
    <button @click="addPersonServer">添加一个人</button>
    <ul>
      <li v-for="p in b.personList" :key="p.id">{{ p.name }}</li>
    </ul>
  </div>
</template>

<script>
import { mapState } from "vuex";
import { nanoid } from "nanoid";
export default {
  name: "myPerson",
  data() {
    return {
      name: "",
    };
  },
  computed: {
    ...mapState(["a", "b"]),
    firstPersonName() {
      return this.$store.getters["b/firstPersonName"];
      //有.就不能有/,可以用[]代替.
    },
  },
  methods: {
    add() {
      const personObj = { id: nanoid(), name: this.name };
      this.$store.commit("b/ADD_PERSON", personObj);
      this.name = "";
      //输完之后框清除
    },
    addWang() {
      const personObj = { id: nanoid(), name: this.name };
      this.$store.dispatch('b/addPersonWang', personObj);
      this.name = "";
    },
    addPersonServer(){
      this.$store.dispatch('b/addPersonServer')
    }
  },
};
</script>

<style>
</style>
myCount.vue
<template>
  <div>
    <h2>当前求和为:{{ sum }}</h2>
    <h2>当前和乘十为:{{ bigSum }}</h2>
    <h2>我在{{ school }}里学习{{ subject }}</h2>
    <h3>下方组件的总人数是:{{personList.length}}</h3>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
      <!-- 这里除了最开始设置的数字1,其他都是字符串所以都加不了了 -->
      <!-- 所以加 :value,当成js表达式去解析 或者加.number强制转换-->
    </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: "myCount",
  data() {
    return {
      n: 1, //用户选择的数字
    };
  },
  computed: {
    ...mapState('a',['sum','school','subject']), 
    ...mapState('b',['personList']), 
    //sum: "sum", school: "school", subject: "subject" }),
    //...mapState('a',['a', 'b']),
   ...mapGetters('a',['bigSum']),
  },
  methods: {
    ...mapMutations('a',{ increment: "JIA", decrement: "JIAN" }),
    ...mapActions('a',{ incrementOdd: "jiaOdd", incrementWait: "jiaWait" }),
  },
};
</script>

<style>
button {
  margin-left: 5px;
}
</style>
myCount.js
//求和相关的配置
const countOptions={
    namespaced:true,
    actions:{
        jiaOdd(context,value) {
            if(context.state.sum%2)
            {
                context.commit('JIA',value)
                //这儿不用写JIAODD因为调用的都是jia,然后下面也就不用加JIAODD了
            }
        },
        jiaWait(context,value) {
            setTimeout(()=>{
                context.commit('JIA',value)
            },500)
        },
    },
    mutations:{
        JIA(state,value) {
            state.sum+=value
        },
        JIAN(state,value) {
            state.sum-=value
        },
    },
    state:{
        sum: 0,
        school:'bj',
        subject:'qd',
    },
    getters:{
        bigSum(state){
            return state.sum*10
        }
    },
}
export default countOptions
myPerson.js
import axios from "axios"
//import { response } from "express"
import { nanoid } from "nanoid"


//人员相关的配置
const personOptions={
    namespaced:true,
    actions:{
        addPersonWang(context,value){
            if(value.name.indexOf('王')==0)
            {
                context.commit('ADD_PERSONSE',value)
                //如果包括王而且位置还是第一个
            }else{
                alert ('添加的人得姓王!')
            }
        },
        addPersonServer(context){
            axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
                response=>{
                    context.commit('ADD_PERSONSE',{id:nanoid(),anme:response.data})
                },
                error=>{
                    alert(error.message)
                }
            ) 
        }
    },
    mutations:{
        ADD_PERSONSE(state,value){
            //value就是人的对象obj 
            state.personList.unshift(value)
            //往前放
        }
    },
    state:{
        personList:[
            {id:'001',name:'tt'}
        ]
    },
    getters:{
        firstPersonName(state){
            return state.personList[0].name
            //这里的state是局部的
        }
    },
}
export default personOptions

  • 29
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值