使用vuex实现任意组件间通信

发布时间 2023-07-19 17:11:37作者: hhhhuaz
title: 使用vuex实现任意组件间通信
date: 2023-07-19 15:51:54
tags:
- vue
categories:
- 工程
- 前端
top:

使用vuex实现任意组件间通信

学习vue的第五天,学到了用插件vuex来实现vue任意组件之间的通信。

以下是个人理解,如有错误请指正。

vuex描述

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

vuex实现了共享数据的功能,当一个组件想要获取数据时直接向vuex发送请求即可,当一个组件想修改vuex中的数据时可以通过显式调用dispatch和commit来对vuex管理的状态进行修改。

image-20230719155810712

这个图片vuex官网贴的一张vuex内部逻辑图片,左边绿色的是组件

  • 当一个组件想要修改vuex管理的数据(state)时,可以 通过调用dispatch方法,需要传递两个参数,第一个是string类型的传入的是Actions中的方法名,第二个是要向方法中传递的参数value。
  • 当Actions接收到一个dispatch时,会向自身去寻找是否有第一个参数的方法名,如果没有就提示错误,找到之后就将第二个参数传入该方法。在Actions中的方法一般有两个参数,第一个是context也就是将vuex本身的一些方法包括(commit、state、dispatch······)这些传给方法,方便在逻辑处理之后进行下一步操作,第二个参数就是value,也就是用户传入的数据,如果是单数据就可以直接用,多数据的话可以传对象。
  • 在Actions中处理逻辑之后一方面可以再次调用dispatch进行下一个阶段的逻辑处理,另一个是直接调用commit将操作发送到Mutations中。当然组件也可以直接通过commit方法向Mutations传。这里有两个参数,第一个是Mutations中的方法名(string),第二个是要操作的数据。
  • Mutations中的方法在检测到有调用时会收到两个参数,第一个是state,也就是vuex储存的数据,第二个是组件要传入的数据。这时就可以进行操作将state中的数据进行更新。
  • 当state中的数据更新后,用到数据的组件也会进行重新的渲染。

由于整个流程中只有Mutations中的方法在真正的操作state中的数据,所以vue开发者工具的监测只针对Mutations。在Actions中可以进行一些复杂的逻辑操作,也可以向其他的服务器发送信息等来验证自己的数据是否合理。

vuex安装配置

上面整理完vuex的工作流程,这里是vuex的安装。

通过npm install vuex@3进行安装即可,这里有一个点是如果使用的是vue2.0的版本,那么vuex就应该下载3.0版本,因为vuex的4.0版本是为vue3.0服务的,所以在npm安装的时候要注意一下选择版本号。

安装完成之后就可以开始配置了。

因为vuex是一个全局的状态管理,有点类似全局事件总线,所以应该将vuex安装在vue实例对象身上,这样任意组件都可以访问到vuex。vuex在vue的配置中叫store,在创建vue实例的时候将其配置到对象中即可。

image-20230719162032157

可以看到这里有一个store是通过外部引入的,这个就是vuex的配置文件,在src下创建一个store文件夹,文件夹里创建一个index.js文件用来写vuex的配置。

插件的安装需要用到Vue.use(),方法,所以在这个文件中也要引入vue。引入vuex之后,可以开始配置了。

上面图中看到vuex的store中有三个主要的内容也可以说是配置对象,就是actions、mutations、state,在文件中创建三个对象

  • actions中要写的是用于响应组件中的动作回调函数,组件通过dispatch方法调用actions中的方法,回调函数接收到的参数有两个(context, value),可以完成一些逻辑,然后将操作传向mutations。
  • mutations中写的是操作state的函数,会被actions和组件通过commit调用,回调函数接收到的参数有两个(state, value)。
  • state中写的就是储存的数据。
  • 下面代码也提到了getters配置项,它就像组件中的computed计算属性,将一些计算后的数据返回,组件可以通过getters点得到想要的内容。

写完配置项之后就可以创建store也就vuex实例了,调用Vuex的Store方法,传入前面写好的四个配置项,然后将new出来的store默认暴露即可,之后在main.js中配置到Vue实例对象上,就可以发现实例对象身上出现了一个$store的值,打开之后就是vuex的方法和state数据。

// store/index.js内容
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
// 使用vuex插件
Vue.use(Vuex)

// 准备actions----用于响应组件中的动作
const actions = {
    incrementOdd(context, value){
        if(context.state.sum % 2) {
            context.commit('INCREMENT', value)
        }
    },
    incrementWait(context, value){
        setTimeout(() => {
            context.commit('INCREMENT', value)
        }, 500);
    },
    addWangStudent(context, value){
        if(value.name[0]!=='王'){
            alert('请添加姓王的同学')
        } else {
            context.commit('ADDSTUDENT', value)
        }
    }
}
// 准备mutations----用于操作数据(state)
const mutations = {
    INCREMENT(state, value){
        state.sum += value
    },
    DECREMENT(state, value){
        state.sum -= value
    },
    ADDSTUDENT(state, value) {
        state.students.unshift(value)
    }
}
// 准备state----用于储存数据
const state = {
    sum:0,
    students:[
        {id:'001', name:'张三'}
    ],
}
// 准备getters----用于将state中的数据进行加工
const getters = {
    bigSum(state){
        return state.sum * 10
    }
}

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

vuex使用

单模块

配置完之后就可以使用了,在组件中通过$store拿到vuex的数据。

  • 拿到state数据中的sum数据

    this.$store.state.sum
    
  • 拿到getters中的数据

    this.$store.getters.bigSum
    
  • 向actions发送

    this.$store.dispatch('incrementOdd', this.n)
    
  • 向mutations发送

    this.$store.commit('DECREMENT',this.n)
    

上面是最基本的拿到store中的数据。

下面是去拿这些数据的简写方法也就是vuex的一些封装好的方法

  • 导入方法

    import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
    
  • 拿state和getters数据(将数据通过计算属性拿到之后就可以在模板中直接调用),这里mapState和mapGetters拿到的数据是函数对象,通过...将对象展开到计算属性中。

    computed: {
        // 借助mapState生成计算属性,从state中读取数据。(对象写法)
        // ...mapState({sum:'sum'})
        // 借助mapState生成计算属性,从state中读取数据。(数组写法)
        ...mapState(['sum']),
    
        // 借助mapGetters生成计算属性,从getters中读取数据。(对象写法)
        // ...mapGetters({bigSum:'bigSum'})
        // 借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  • 通过mapMutations和mapActions生成调用函数,这里生成的函数可以有传参,不过需要在标签调用时直接传入,否则会将事件event传入。

    methods: {
        // 自己写的方法调用commit
        // increment(){
        //     this.$store.commit('INCREMENT',this.n)
        // },
        // decrement(){
        //     this.$store.commit('DECREMENT',this.n)
        // },
    
        // 借助mapMutations生成对应的方法,方法会调用commit方法联系mutations(对象写法)
        // 需要调用时传参value,否则传参为event
        ...mapMutations({increment:'INCREMENT', decrement:'DECREMENT'}),
        // 借助mapMutations生成对应的方法,方法会调用commit方法联系mutations(数组写法)
        // ...mapMutations(['INCREMENT','DECREMENT'])
    
        // 自己写的方法调用dispatch
        // incrementOdd(){
        //     this.$store.dispatch('incrementOdd', this.n)
        // },
        // incrementWait(){
        //     this.$store.dispatch('incrementWait', this.n)
        // },
        // 借助mapActions生成对应的方法,方法会调用dispatch方法联系actions(对象写法)
        // 需要调用时传参value,否则传参为event
        // ...mapActions({incrementOdd:'incrementOdd', incrementWait:'incrementWait'}),
        // 借助mapActions生成对应的方法,方法会调用dispatch方法联系actions(数组写法)
        ...mapActions(['incrementOdd','incrementWait'])
    },
    
多模块

上面的store配置是单模块的,如果多人开发,就会出现冲突问题,下面通过命名空间多模块,来解决。

index.js中

  • 将两个组件的数据分别配置在两个对象中,打开namespaced配置为true,在下面new store时传入模块。
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
// 使用vuex插件
Vue.use(Vuex)

const countOptions = {
    namespaced:true,
    actions:{
        incrementOdd(context, value){
            if(context.state.sum % 2) {
                context.commit('INCREMENT', value)
            }
        },
        incrementWait(context, value){
            setTimeout(() => {
                context.commit('INCREMENT', value)
            }, 500);
        },
    },
    mutations:{
        INCREMENT(state, value){
            state.sum += value
        },
        DECREMENT(state, value){
            state.sum -= value
        },
    },
    state:{
        sum:0,
    },
    getters:{
        bigSum(state){
            return state.sum * 10
        }
    }
}
const studentOptions = {
    namespaced:true,
    actions:{
        addWangStudent(context, value){
            if(value.name.indexOf('王')===0){
                context.commit('ADDSTUDENT', value)
            } else {
                alert('请添加姓王的同学')
            }
        }
    },
    mutations:{
        ADDSTUDENT(state, value) {
            state.students.unshift(value)
        }
    },
    state:{
        students:[
            {id:'001', name:'张三'}
        ],
    },
    getters:{
        firstStudentName(state){
            return state.students[0].name
        }
    }
}
export default new Vuex.Store({
    modules: {
        countOptions,
        studentOptions
    }
})

组件中调用时

  • 简写方式,在mapState函数中传入第一个参数,也就是模块的命名名称。

    ...mapState('countOptions',['sum']),
    ...mapGetters('countOptions',['bigSum']),
    ...mapMutations('countOptions',{increment:'INCREMENT', decrement:'DECREMENT'}),
    ...mapActions('countOptions',['incrementOdd','incrementWait']),
    
  • 未简写方式

    // 获取state中的数据时需要先获取命名对象,再获取想要的数据
    this.$store.state.countOptions.sum
    // 获取getters时需要在getters的对象名称前加上命名名称/
    this.$store.getters['studentOptions/firstStudentName']
    // 向actions和mutations传时需要修改传入的第一个参数,在方法名前加上命名名称/
    this.$store.commit('studentOptions/ADDSTUDENT', studentObj)
    this.$store.dispatch('studentOptions/addWangStudent', studentObj)