Vuex

103 阅读3分钟

1.vuex概述

  • vuex是实现数据集中式状态管理插件。数据由vuex统一管理。其他组件都去使用vuex中的数据
  • 只要有其中一个组件去修改了这个共享的数据,其他组件就会同步更新。
  • 注意:全局事件总线和vuex插件的区别:
    • 1.全局事件总线关注点:组件和组件之间数据如何传递,一个绑定$on,一个触发$emit。数据实际上还是在局部组件中,并没有真正实现数据共享,只是数据传来传去

image.png

  • 2.vuex插件的关注点:共享数据本身就在vuex上。其中任何一个组件去操作这个数据,其他组件都会同步更新,是真正意义的数据共享。

image.png

2.vuex 环境搭建

1.安装 vuex

  • vue2安装vue3版本
    • npm i vuex@3
  • vue3安装vuex4版本
    • npm i vuex@4

2.创建目录和js文件(目录和文件名不是必须叫这个)

  • 目录:vuex
  • js文件:store.js

3.在store.js文件中创建核心store对象,并暴露

image.png

4.在 main.js 文件中关联 store,这一步很重要,完成这一步之后,所有的 vm 和 vc 对象上会多一个store属性

image.png

3.vuex实现一个最简单的案例

  • 使用vuex实现一个点我加一的简单功能
  • App.vue
 <div>
		<h1>数字:{{$store.state.num}}</h1>
		<button @click="plusOne">点我加1</button>
    </div>
    methods: {
			plusOne(){
				// 这里的代码在实际开发中可能会比较复杂。
				// 业务逻辑复杂,代码比较多。
				// 如果你将这些代码放到这里的话,这些业务逻辑代码无法得到复用。
				// 无法在其他组件中使用,在其他组件中使用的时候,你还需要把这些代码再写一遍。
				//this.$store.state.num++
				// 调用vuex的API。
				// dispatch是vuex的API。调用这个方法之后,store对象中的plusOne这个action回调函数会被自动调用。
				// dispatch:分发
				// 交给plusOne这个action去处理这个事儿。
				this.$store.dispatch('plusOne', this.startNum)
			}
		}
  • store.js

const actions = {
    // N多个action
    // 每一个action都是一个callback(回调函数)
    // 在action这种回调函数中编写复杂的业务逻辑
    // 有个原则:action是专门用来处理业务逻辑,或者说发送AJAX请求的。
    //plusOne : function(){}
    // 简写
    // context参数:context是vuex的上下文(context可以看做是store对象的缩小版)
    // value参数:传过来的数据
    plusOne(context, value){ 
        // 处理业务
        value = value + 1
        // 调用其它的action这个回调函数
        //context.dispatch('otherAction', value)
        // 业务逻辑处理完成之后,继续向下一个环节走,就轮到了数据的更新。
        // 提交上下文环境(所有的事儿都做完了,该最后一步了,更新数据了,怎么办?提交)
        context.commit('PLUS_ONE', value)
    },

    // 这里可能还会有其它的action
    // ...
    /* otherAction(context, value){        	
        console.log(6666)
    } */
}
const mutations = {
    // N多个mutation
    // 每一个mutation都是一个callback(回调函数)
    // 每一个mutation这个回调函数的作用就是:更新state
    // 只要state一更新,因为是响应式的,所以页面就重新渲染了。
    // state参数:状态对象
    // value参数:上一环节传过来的数据
    PLUS_ONE(state, value){
        state.num += value
    }
}

// 等同于Vue当中的data(只不过这里我们不叫做数据,叫做状态)
// 状态对象(数据对象),已经做了响应式处理的。
const state = {
    num : 0
}
  • 为什么要这么折腾?
    • 通过以上案例,可以看出num可以被多个组件共享(vuex可以管理多个组件共享的数据)
    • 通过$on$emit 这种全局事件总线不好吗?可以。但如果组件多的话,并且涉及到读和写的操作会导致混乱。
  • actions中的回调函数,参数context
    • 如果业务逻辑非常复杂,需要多个actions中的方法联合起来才能完成,可以在回调函数中使用context继续调用dispatch方法触发下一个action方法的执行
   // 如果action中的逻辑非常简单,可以不经过action,直接通过commit走mutation
this.$store.commit('SAVE_USER', {id:Date.now(),name:this.username})

const actions = {
    /* saveUser(context, value){
        context.commit('SAVE_USER', value)
    },
    saveVip(context, value){
        context.commit('SAVE_VIP', value)
    } */
}
const mutations = {
    SAVE_USER(state, value){
        state.users.unshift(value)
    },
    SAVE_VIP(state, value){
        state.vips.unshift(value)
    }
}

4.vuex工作原理

  • 如果业务逻辑非常简单,也不需要发送 AJAX 请求的话,可以不调用 dispatch 方法,直接调用 commit 方法也是可以的

image.png

image.png

5.多组件数据共享

1.实现以下功能

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/87b1675964034007a5d5576b93e88cb1~tplv-k3u1fbpfcp-watermark.image?)

image.png

  • 核心代码:
    • User.vue
<template>
    <div>
        <h1>用户列表</h1>
        <input type="text" v-model="username">
        <button @click="saveUser">保存用户</button>
        <ul>
            <li v-for="user in $store.state.users" :key="user.id">
                用户名:{{user.name}}
            </li>
        </ul>
        <h3>当前用户数量:{{$store.state.users.length}}</h3>
        <h3>当前会员数量:{{$store.state.vips.length}}</h3>
    </div>
</template>

<script>
	export default {
		name : 'User',
        data() {
            return {
                username : '',
            }
        },
        methods: {
            saveUser(){
                // dispatch
                this.$store.dispatch('saveUser', {id:Date.now(),name:this.username})
            }
        },
	}
</script>
  • Vip.vue
<template>
    <div>
        <h1>会员列表</h1>
        <input type="text" v-model="vipname">
        <button @click="saveVip">保存会员</button>
        <ul>
            <li v-for="vip in $store.state.vips" :key="vip.id">
                会员名:{{vip.name}}
            </li>
        </ul>
        <h3>当前用户数量:{{$store.state.users.length}}</h3>
        <h3>当前会员数量:{{$store.state.vips.length}}</h3>
    </div>
</template>

<script>
	export default {
		name : 'Vip',
        data() {
            return {
                vipname : ''
            }
        },
        methods: {
            saveVip(){
                // dispatch
                this.$store.dispatch('saveVip', {id:Date.now(), name:this.vipname})
            }
        },
	}
</script>
  • stroe.js
import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex)

const actions = {
    saveUser(context, value){
        context.commit('SAVE_USER', value)
    },
    saveVip(context, value){
        context.commit('SAVE_VIP', value)
    }
}
const mutations = {
    SAVE_USER(state, value){
        state.users.unshift(value)
    },
    SAVE_VIP(state, value){
        state.vips.unshift(value)
    }
}
const state = {
    users : [
        {id:'001', name:'孙悟空'},
        {id:'002', name:'猪八戒'},
        {id:'003', name:'沙和尚'}
    ],
    vips : [
        {id:'001', name:'高启强'},
        {id:'002', name:'高启盛'},
        {id:'003', name:'张三'}
    ]
}

export default new Vuex.Store({actions, mutations, state})
  • main.js
import store from './vuex/store'

new Vue({
  el : '#app',
  store,
  render : h => h(App),
})

2.getters配置项

  • 如果想将state中的数据进行加工计算,并且这个计算逻辑复杂,而且要在多个位置使用,建议使用getters配置项
  • 怎么用? image.png

image.png

  • 类似于 Vue中的:data 和 computed 的关系

3.mapState 和 mapGetters的使用(优化计算属性)

  1. 组件中在使用state上的数据和getters上的数据时,都有固定的前缀
    • {{this.$store.state.name}}
    • {{this.$store.getters.reverseName}}
    • 使用mapState 和 mapGetters进行名称映射,可以简化以上写法
  2. 使用mapState 和 mapGetters的前提是先引入
    • import {mapState,mapGetters} form 'vuex'
  3. mapState如何使用,在computed中使用ES6语法:...运算符进行引入
    • computed是一个对象,在对象中合并属性使用...运算符
    • 第一种方式:对象形式:
      • ...mapGetters({name:"name"})
    • 第二种方式:数组形式:
      • ...mapGetters(["name"])
      • 只有属性名和属性值一致时才可以使用数组形式
    • 插值语法就可以修改为:{{name}}

4.mapMutations 和 mapActions的使用(优化methods)

  • import {mapMutations,mapActions} from 'vuex'
methods:{
    //对象写法
    ...mapActions({add:'plusOne',reverseName:'reverseName'})
    //数组写法(前提是:保证 methods 中的方法名和一致)
  ...mapActions(['plusOne','reverseName'])

6.vuex的模块化开发

1. 未使用mapXxxx的模块化开发

  • a模块 image.png
  • b模块 image.png
  • c模块 image.png
  • 在store.js中引入各个模块 image.png
  • A组件 image.png
  • B组件 image.png
  • 将A组件和B组件在App组件注册 image.png

2.使用mapXxxx的模块化开发

  • a模块 image.png
  • b模块 image.png
  • 在store.js中引入a和b模块 image.png
  • A组件 image.png
  • B组件 image.png
  • 在App组件中注册A组件和B组件 image.png
  • 在action中发送ajax请求:

image.png