Vuex的使用

Vuex

简介

Vuex是一个专为Vue.js 应用程序开发的==状态管理模式==。

它采用==集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex也集成到Vue的官方调试工具
devtools extension==,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

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

Vuex的最主要的作用:实现多组件之间的数据共享

image20230929121742248.png

什么时候使用 Vuex

  1. 多个组件依赖于同一状态

  2. 来自不同组件的行为需要变更同一状态

1、安装:

注意:

  • vue2中,要用vuex的3版本。
  • vue3中,要用vuex的4版本。
#vue2版本安装
npm install -save vuex@3

#vue3版本安装
npm install -save vuex@4

2、使用:在src目录下创建store目录,然后再store目录创建index.js文件

image20230929115901256.png

index.js

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)


//准备actions——用于响应组件中的动作
const actions = {
	  
}

//准备mutations——用于操作数据(state)
const mutations = {

}

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


// 定义getter来获取数据
const getters = {
	
}

export default new Vuex.Store({
	actions,
	mutations,
    state,
    getters,
})

3、再main.js文件导入store/index.js文件

image20230929120854868.png

import Vue from 'vue'
import App from './App'
import router from './router'
//引入store
import store from './store'

Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

简单案例(求和)

纯Vue版写的

<template>
  <div class="hello">
    <h1>当前sum的值为{{ sum }}</h1>
    <select v-model.number="num">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <hr>
    <button @click="increment">++</button>
    <button @click="decrement">--</button>
    <button @click="incrementOne">为奇数则加1</button>
    <button @click="incrementTow">为偶数则加2</button>
  </div>
</template>

<script>
export default {
  name: 'Count',
  data () {
    return {
      sum: 0,
      num: 1
    }
  },

  methods: {
    increment() {
      this.sum += this.num
    },

    decrement() {
      this.sum -= this.num
    },

    incrementOne() {
      if (this.sum % 2 != 0) { 
          this.sum += 1
      }
    },
    incrementTow() {
      if (this.sum % 2 == 0) {
          this.sum += 2
      }
    },
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

效果

image20230929130905209.png

使用Vuex实现

action

//准备actions——用于响应组件中的动作
const actions = {
    //commit是一个对象,可以获取到state状态中对象的属性值,这里是sum。state为传入的值
    increment(commit,state) {
        console.log("increment",commit);
    }
	  
}

在这里插入图片描述

mutation

//准备mutations——用于操作数据(state)
const mutations = {
    //commit是一个对象,可以获取到state状态中对象的属性值,这里是sum。state为传入的值
    increment(commit,state) {
        console.log("increment",commit);
        console.log("increment",state);
    }
}

image20230929144624544.png

state

const state = {
  sum: 0
}
完整的index.js
方式一(不推荐使用)

原因:这种的话数据使用vue的开发者工具就不奏效了

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)


//准备actions——用于响应组件中的动作
const actions = {
    increment(commit, state) {
        console.log("increment",commit);
        commit.state.sum += state
    },
    decrement(commit,state) {
        commit.state.sum -= state

    },
    incrementOne(commit,state) {
        commit.state.sum += state
    },
    incrementTow(commit,state) {
        commit.state.sum += state
    }
	  
}

//准备mutations——用于操作数据(state)
const mutations = {
    increment(commit,state) {
        commit.state.sum += state
    },
    decrement(commit,state) {
        commit.sum -= state

    },
    incrementOne(commit,state) {
        commit.state.sum += state
    },
    incrementTow(commit,state) {
        commit.state.sum += state
    }
}

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


// 定义getter来获取数据
const getters = {
	
}

export default new Vuex.Store({
	actions,
	mutations,
    state,
    getters,
})
方式二(推荐使用)
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)


//准备actions——用于响应组件中的动作
const actions = {
    increment(context, state) {
        console.log("increment",context);
        context.commit('INCREMENT', state)
    },
    decrement(context,state) {
        context.commit('DECREMENT',state)

    },
    incrementOne(context,state) {
        context.commit('INCREMENTONE',state)
    },
    incrementTow(context,state) {
        context.commit('INCREMENTTOW',state)
    }
	  
}

//准备mutations——用于操作数据(state)
const mutations = {
    INCREMENT(commit,state) {
        commit.sum += state
    },
    DECREMENT(commit,state) {
        commit.sum -= state

    },
    INCREMENTONE(commit,state) {
        commit.sum += state
    },
    INCREMENTTOW(commit,state) {
        commit.sum += state
    }
}

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


// 定义getter来获取数据
const getters = {
    sum: state => state.sum,
}

export default new Vuex.Store({
	actions,
	mutations,
    state,
    getters,
})
VueComponent调用
方式一:通过dispatch调用
<template>
    <div class="hello">
        <h1>当前sum的值为{{ $store.state.sum }}</h1>
        <select v-model.number="num">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <hr>
        <button @click="increment">++</button>
        <button @click="decrement">--</button>
        <button @click="incrementOne">为奇数则加1</button>
        <button @click="incrementTow">为偶数则加2</button>
    </div>
</template>

<script>
export default {
    name: 'Count',
    data() {
        return {
            num: 1
        }
    },

    methods: {
        increment() {
            this.$store.dispatch('increment', this.num)
        },

        decrement() {
            this.$store.dispatch('decrement', this.num)
        },

        incrementOne() {
            if (this.$store.state.sum % 2 != 0) {
                this.$store.dispatch('incrementOne', 1)
            }
        },
        incrementTow() {
            if (this.$store.state.sum % 2 == 0) {
                this.$store.dispatch('incrementTow', 2)
            }
        },
    },

    mounted() {
        console.log("app", this);
    }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>
方式二:通过commit调用
<template>
    <div class="hello">
        <h1>当前sum的值为{{ $store.state.sum }}</h1>
        <select v-model.number="num">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <hr>
        <button @click="increment">++</button>
        <button @click="decrement">--</button>
        <button @click="incrementOne">为奇数则加1</button>
        <button @click="incrementTow">为偶数则加2</button>
    </div>
</template>

<script>
export default {
    name: 'Count',
    data() {
        return {
            num: 1
        }
    },

    methods: {
        increment() {
            this.$store.commit('INCREMENT', this.num)
        },

        decrement() {
            this.$store.commit('DECREMENT', this.num)
        },

        incrementOne() {
            if (this.$store.state.sum % 2 != 0) {
                this.$store.commit('INCREMENTONE', 1)
            }
        },
        incrementTow() {
            if (this.$store.state.sum % 2 == 0) {
                this.$store.commit('INCREMENTTOW', 2)
            }
        },
    },

    mounted() {
        console.log("app", this);
    }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>

效果

image20230929160957686.png

Vuex 工作原理图

image20230925233024945.png

  • Vue Components:Vue组件

  • Actions:行为或动作

    • 数据类型:对象(多个),一般是函数,如:

      //准备actions——用于响应组件中的动作
      const actions = {
      	
          // 帐号登录, 从后端获取用户数据,并调用mutations来更新状态
          Login({ commit }, loginForm) {
            return new Promise((resolve, reject) => {
              login(loginForm).then(res => { 
              // console.log(res);
                if (res.code === 200) { 
                commit('SET_USER', res.data)
              }
      				// 传递给welcome.vue : store.dispatch('Login').then(data)
                resolve(res)	
                }
      			 ).catch(error => {
                reject(error)
              })
            })
          },   
      }
      
  • Mutations:字面意思为修改

    • 数据类型:对象(多个),一般是函数,如:

      //准备mutations——用于操作数据(state)
      const mutations = {
      
        SET_USER: (state, user) => {
          // console.log(`mutations中的SET_USER被调用了`);
          state.user.id = user.id
          state.user.nickName = user.nickName
          state.user.email = user.email
          state.user.password = user.password
          state.user.phone = user.phone
          state.user.qqOpenId = user.qqOpenId
          state.user.avatar = user.avatar
        },
      }
      
  • state:状态

    • 数据类型:对象(多个),如:

      //准备state——用于存储数据
      const state = {
        user: {}, //当前用户信息
        token: getToken(),//当前用户的token
        newArticle: [], //最新文章
      }
      
  • Actions、Mutations和state由store来管理。

    image20230929141535640.png

一般的调用过程解释:

  • Vue组件通过调用dispatch(参数1,参数2)函数来调用Actions中的函数对象,参数1为Actions中的方法名并用''单引号包裹,方法名为Actions中的方法,参数2为传入的数据。调用过程:Vue实例对象.$store.dispatch(function,数据),如:this.$store.dispatch('Login',userForm)

  • Actions通过commit(参数1,参数2)函数调用Mutations的函数对象,参数1为Mutations中的方法名并用''单引号包裹,参数2为传入的数据。调用过程:commit(function,数据),如:commit('SET_USER',userForm)

  • 同时Vue组件也可以通过commit(参数1,参数2)函数调用Mutations的函数对象,参数1为Mutations中的方法名并用''单引号包裹,方法名为Mutations中的方法,参数2为传入的数据。调用过程:Vue实例对象.$store.commit(function,数据),如:this.$store.commit('SET_USER',userForm)

  • Vue组件可以不直接也Actions交互,Actions一般与后端的接口交互。

state相当于vue组件中的data,而getters相当于vue组件中的computed

getters配置项

// 定义getter----用来对state的数据进行加工
const getters = {
     sum: state => state.sum * 10, //或
    // bugSum(state) {
    //     return state.sum * 10
    // }
}

image20230929161606592.png

image20230929161647849.png

注意:这种方式再获取store中的值时,都需要使用$store.state.xxx$store.getters.xxx来获取值,所以,这种方式比较繁琐。获取store中的值还有其他的方法,如mapState()、mapGetters()、mapActions()、Mutations()。下面将给出。

mapState

导入mapState

import  {mapState} from"vuex"

通过计算属性computed来获取state中的值

//参数为数组的写法
computed: {
    ...mapState(['sum'])
},

   
//参数为对象的写法
computed: {
   ...mapGetters({sum:'sum'})
},

注意:mapState()必须要传参数,且必须是数组或对象,mapState参数要是store中的state中存在的属性,不然取出的值为null

image20230929175752612.png

image20230929172401397.png

image20230929172700867.png

通过this.sum可以在当前vc中使用,原因:通过使用...mapState(['sum']),vc就会拥有sum属性如下,

image20230929175523740.png

效果:

image20230929172739700.png

mapGetters

导入mapGetters

import  {mapGetters} from"vuex"

通过计算属性computed来获取state中的值

//参数为数组的写法
computed: {
    ...mapGetters(['bigSum'])
},
    
//参数为对象的写法
computed: {
   ...mapGetters({bigSum:'bigSum'})
},

注意:mapGetters()必须要传参数,且必须是数组或对象,mapGetters参数要是store中state中存在的属性,不然取出的值为null

image20230929172401397.png

image20230929175925659.png

通过this.bigSum可以在当前vc中使用,原因:通过使用...mapGetters(['bigSum']),vc就会拥有bigSum属性如下,

image20230929180141347.png

image20230929180247122.png

效果:

image20230929180318154.png

mapActions

mapActions方法:用于帮助我们生成与 actions对话的方法,即:包含$store.dispatch(xxx)的函数。借助mapActions生成对应的方法,方法中会调用dispacth去联系actions。

导入mapGetters

import  {mapActions} from"vuex"

mapGetters获取的是方法名,因此要写到methods中

方式一:对象的写法

methods: {
   // 对象的写法
    ...mapActions({jia:'increment', jian:'decrement', jiaOne:'incrementOne', jiaTow:'incrementTow'}),
},

绑定事件

image20230929232645212.png

效果:

image20230929232351726.png

方式二:数组的写法

 methods: {
      ...mapActions( ['INCREMENT', 'DECREMENT',  'INCREMENTONE',  'INCREMENTTOW'] ),
}

事件绑定

image20230929231856380.png

效果:

image20230929232049455.png

mapMutations

mapMutations方法:用于帮助我们生成与 mutations对话的方法,即:包含$store.commit(xxx)的函数。借助mapMutations生成对应的方法,方法中会调用commit去联系mutations。

导入mapGetters

import  {mapMutations} from"vuex"

通过计算属性computed来获取state中的值

//参数为数组的写法
computed: {
    ...mapGetters(['bigSum'])
},
    
//参数为对象的写法
computed: {
   ...mapGetters({bigSum:'bigSum'})
},

注意:mapGetters()必须要传参数,且必须是数组或对象,mapGetters参数要是store中state中存在的属性,不然取出的值为null

方式一:对象的写法

methods: {
       ...mapMutations({ increment: 'INCREMENT', decrement: 'DECREMENT', incrementOne: 'INCREMENTONE', incrementTow: 'INCREMENTTOW' }),
}

image20230929221412150.png

原因:increment,decrement,incrementOne与increment相当于methods中的方法名,让其他的事件调用。

类似于调用this.#stroe.commit('mutation中的方法名',传入的数据),次数mutation中方法的第二个参数(即传入的数据参数为PointerEvent鼠标事件对象)

image20230929222425424.png

出现这种状况的原因:当由vc通过调用this.$store.commit('INCREMENT',this.n)时才是正确的,

methods:{
	increment(){
		this.$store.commit('INCREMENT',this.n)
	}
}

绑定的事件为:

image20230929223132581.png
而当使用...mapMutations({ increment: 'INCREMENT',...}),是调用成

methods:{
	increment(value){
		this.$store.commit('INCREMENT',value)
	}
}

绑定的事件并没有传入参数,因此increment方法传入的参数value的值为event事件,所以就产生如上图的当前sum的值为0[object PointerEvent]错误,解决这个错误只需将绑定的事件传入指定的值即可,修改绑定事件如下:

image20230929223916172.png
解决后的效果:

image20230929224105717.png

嵌套函数的写法(不推荐):

methods: {
        ...mapMutations({ jia: 'INCREMENT', jian: 'DECREMENT', jianOne: 'INCREMENTONE', jianTow: 'INCREMENTTOW' }),

        increment() {
            this.jia(this.num);
        },

        decrement() {
            this.jian(this.num)
        },

        incrementOne() {
            if (this.$store.state.sum % 2 != 0) {
                this.jianOne(1)
            }
        },
        incrementTow() {
            if (this.$store.state.sum % 2 == 0) {
                this.jianTow(2)
            }
        },
    },

绑定事件

image20230929224931505.png
效果:

image20230929225002931.png

方式二:数组的写法

methods: {
        ...mapMutations(['INCREMENT', 'DECREMENT','INCREMENTONE', 'INCREMENTTOW' ]),
},

需要注意的是,此时,mapMutations中数组的值必须为actions中的方法名,这时methods中的方法名也变成了actions中的方法名,因此绑定的事件名必须于actions中的方法名一致,并传入指定的参数值。如下

image20230929230225593.png
效果:

image20230929230100630.png

总结: mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

Vuex模块化

countOption.js(求和相关)

const countOption = {
     //开启命名空间
    namespaced: true
    
    actions : {
        
    },

    //准备mutations——用于操作数据(state)
    mutations : {
      
        
    },

    //准备state——用于存储数据
    state : {
        
    },


    // 定义getter----用来对state的数据进行加工
    getters : {
      
    }
}
export default countOption

personOption.js(人员管理相关)

const personOption = {
    //开启命名空间
    namespaced: true
    
    actions : {
        
    },

    //准备mutations——用于操作数据(state)
    mutations : {
      
        
    },

    //准备state——用于存储数据
    state : {
        
    },


    // 定义getter----用来对state的数据进行加工
    getters : {
      
    }
}

export default personOption

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import count from './modules/countOption'
import person from './modules/personOption'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    count,
    person
  },
})

export default store

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

//方式一:直接读取
this.$store.state.person.list
//方式二:借助mapState读取:
...mapState('count',['sum']),

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

//方式一:直接读取
this.$store.getters['person/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('count',['bigSum'])

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

//方式一:直接dispatch
this.$store.dispatch('person/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('count',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

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

//方式一:自己直接commit
this.$store.commit('person/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('count',{increment:'JIA',decrement:'JIAN'}),

本文案例地址:https://github.com/rookiesnewbie/VuexStudy