跳过正文
  1. 文章/
  2. 前端/
  3. Vue/

9、vuex

·3137 字·7 分钟· loading · loading · ·
前端 Vue
GradyYoung
作者
GradyYoung
Vue - 点击查看当前系列文章
§ 9、vuex 「 当前文章 」

vuex是什么
#

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

https://github.com/vuejs/vuex

image-20220420210808376

什么时候是用vuex
#

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

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

工作原理
#

image-20220420213045697

mutations和actions区别
#

Vuex中的mutations用于同步操作,而actions则处理异步操作。以下是它们的具体区别:

  • 同步与异步:
    • Mutations是同步的,这意味着在mutation中的函数执行时,不能包含任何异步操作,如Promise或者setTimeout等。这样可以保证状态变更的追踪和调试相对简单直观。
    • Actions可以包含异步操作,它通过分发(dispatch)来触发,并且最终会提交(commit)一个mutation来变更状态。这使得actions成为处理例如API调用等需要等待响应的操作的理想选择。
  • 修改state的方式:
    • Mutations可以直接修改state,但必须通过提交mutation的方式来进行,通常在组件中通过this.$store.commit('mutationName', payload)来调用。
    • Actions无法直接修改state,它们的主要职责是执行异步操作,然后通过提交mutation来更改状态。在组件中,actions通过this.$store.dispatch('actionName', payload)来触发。

搭建vuex环境
#

1、安装vuex

注意vuex@4版本只能在vue3中使用,如果vue2中使用,需要安装vuex@3

cnpm i vuex@3 

3、src下创建store/index.js,声明store配置项actions、mutations、state,并且Vue使用vuex(必须在store实例化之前,所以不能写在main.js),然后实例化store并导出

//该文件用于创建vuex中核心store
import Vue from 'vue';
import Vuex from 'vuex'
//使用
Vue.use(Vuex);

//用于响应组件的动作,处理业务逻辑,比如请求接口
const actions = {

}

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

}

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

}

//创建并导出store
export default new Vuex.Store({
    actions,
    mutations,
    state
});

4、main.js中引入store,并声明为Vue实例配置项

import store from './store';

new Vue({
    render: h => h(App),
    store
}).$mount("#app")

基本使用
#

store/index.js定义多组件共享数据,并且定义操作数据的方法

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);

const actions = {
    add(context,val){
        //操作业务逻辑
        val += 1;
        //调用mutations中的add
        context.commit("add",val);
    }
}

const mutations = {
    add(state,val){
        //对数据进行操作
        state.num += val;
    }
}

const state = {
    num: 0
}

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

App.vue展示数据,引入两个操作数据的组件

<template>
  <div>
    <!-- 获取state中的num值 -->
    {{$store.state.num}}
    <Comp1/>
    <Comp2/>
  </div>
</template>

<script>
import Comp1 from './components/Comp1.vue';
import Comp2 from './components/Comp2.vue';
export default {
    name: "App",
    components: {
      Comp1,
      Comp2
    }
}
</script>

Comp1.vue调用$store.dispatch,对数据进行业务逻辑操作(actions),然后再更改数据(mutations)

<template>
    <button @click="add2">add2</button>
</template>
<script>
export default {
    name: "Comp1",
    methods: {
        add2(){
            //dispatch调用的是action中add,后面的参数为val值
            this.$store.dispatch("add", 1);
        }
    }
}
</script>

Comp2.vue调用$store.commit直接对数据进行操作(mutations),不需要业务逻辑操作

<template>
    <button @click="add1">add1</button>
</template>
<script>
export default {
    name: "Comp2",
    methods: {
        add1(){
            //commit直接调用mutations中的add函数,跳过action
            this.$store.commit("add",1);
        }
    }
}
</script>

getters配置项
#

类似于组件中声明的计算属性computed,用于计算state中的属性

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);

const actions = {}

const mutations = {}

const state = {
    num: 9
}

const getters = {
    tenfold(state){
       	return state.num * 10;
    }
}

export default new Vuex.Store({
    actions,
    mutations,
    state
});
<template>
  <div>
    <!-- 获取 -->
    {{$store.getters.tenfold}}
  </div>
</template>

<script>
export default {
    name: "App"
}
</script>

生成组件配置
#

在组件里面,我们一直使用this.$store.state.属性来获取属性或this.$store.getters.函数名来获取计算属性结果,过于麻烦,所以vuex提供了一种map...来帮助快速生成计算属性

mapState
#

用于将state中的属性,在组件中声明为计算属性computed的配置,简化编码

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);

const actions = {}

const mutations = {}
// 声明两个属性
const state = {
    id: "123",
    name: "lucy"
}

const getters = {}

export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
});
<template>
  <div>
    <!-- 直接使用计算属性即可 -->
    <!-- id : {{userId}}, name : {{userName}} -->
    id : {{id}}, name : {{name}}
  </div>
</template>

<script>
//引入mapState
import {mapState} from 'vuex';
export default {
    name: "App",
    computed: {
      // 1、对象写法,用于需要给state中的属性重新起名
      // ...mapState({userId:'id',userName:'name'})
      // 对象写法的属性值也可以是一个函数,例如,引入state中conf中的timer
      // (非常有用)例如需要在方法中获取该对象的m
      // ...mapState({'timer':(state)=>state.conf.timer}),
      // 2、数组写法,属性名就是state中的属性名
      ...mapState(['id','name'])
    }
}
</script>

mapGetters
#

用于将getters中的属性,在组件中声明为计算属性computed的配置,简化编码

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);

const actions = {}

const mutations = {}

const state = {
    num: 9
}

const getters = {
    tenfold(state){
        return state.num * 10;
    }
}

export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
});
<template>
  <div>
    <!-- 直接使用计算属性即可 -->
    <!-- {{multi10}} -->
    {{tenfold}}
  </div>
</template>

<script>
//引入mapState
import {mapGetters} from 'vuex';
export default {
    name: "App",
    computed: {
      // 1、对象写法,用于需要给getters中的属性重新起名
      // ...mapGetters({multi10:'tenfold'})
      // 2、数组写法,计算属性名就是getters中的属性名
      ...mapGetters(['tenfold'])
    }
}
</script>

mapActions
#

替代调用this.$store.dispatch(),生成对应actions中的方法,作为组件methods配置

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);

const actions = {
    add(context,val){
        val += 1;
        context.commit("add",val);
    }
}

const mutations = {
    add(state,val){
        state.num += val;
    }
}

const state = {
    num: 0
}

const getters = {}

export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
});
<template>
  <div>
    {{$store.state.num}}
    <!-- 此处的add是mapActions构造的这个函数默认有一个参数 -->
    <!-- 如果就是add的val参数如果不传默认是event对象 -->
    <button @click="add(1)">add</button>
  </div>
</template>

<script>
//引入mapState
import {mapActions} from 'vuex';
export default {
    name: "App",
    methods: {
      //和mapState一样,同样支持对象或数组的写法
      ...mapActions(['add'])
    },
}
</script>

mapMutations
#

替代调用this.$store.commit(),生成对应mutations中的方法,作为组件methods配置

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);

const actions = {}

const mutations = {
    add(state,val){
        state.num += val;
    }
}

const state = {
    num: 0
}

const getters = {}

export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
});
<template>
  <div>
    {{$store.state.num}}
    <!-- 此处的add是mapMutations构造的这个函数默认有一个参数 -->
    <!-- 如果就是add的val参数如果不传默认是event对象 -->
    <button @click="add(1)">add</button>
  </div>
</template>

<script>
//引入mapState
import {mapMutations} from 'vuex';
export default {
    name: "App",
    methods: {
      //和mapState一样,同样支持对象或数组的写法
      ...mapMutations(['add'])
    },
}
</script>

vuex的模块化开发
#

在实际开发中,如果大型项目,那么可能在actions、mutations、state、getters中,多种业务的数据、逻辑都写在同一个对象中,增大了耦合,而且不容易维护。所以,vuex提供了模块化的开发方法,可以将每个业务的actions、mutations、state、getters单独的写在一个对象(文件导入)中,互不影响,组件在使用的时候,可以按照需要对namespace使用map...进行引入

例如,有支付、订单两个业务:

payOptions.js用于写支付业务的actions、mutations、state、getters

export default{
    namespaced: true, //开启namespaced,才可以按照名称进行引入
    state: {
        money: 10000
    },
    actions: {
        add(context,val){
            context.commit('add',val);
        }
    },
    mutations: {
        add(state,val){
            state.money += val
        }
    },
    getters: {

    }
}

orderOptions.js用于写订单业务的actions、mutations、state、getters

export default {
    namespaced: true, 
    state: {
        totalOrder: 1314
    },
    actions: {
        sub(context,val){
            context.commit('sub',val);
        }
    },
    mutations: {
        sub(state,val){
            state.totalOrder -= val;
        }
    },
    getters: {

    }
}

index.js,引入每个模块的文件,并配置到store

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);
// 引入业务模块
import payOptions from './payOptions';
import orderOptions from './orderOptions';

export default new Vuex.Store({
    //配置业务模块到store
    modules: {
        payOptions,
        orderOptions
    }
});

App.vue,分别引入每个模块的state、actions进行测试

<template>
  <div>
    剩余资金{{money}}剩余订单{{totalOrder}}<br/>
    <button @click="add(100)">addMoney</button>
    <button @click="sub(10)">subOrder</button>
  </div>
</template>

<script>
//引入mapState
import {mapState,mapActions} from 'vuex';
export default {
    name: "App",
    methods: {
      ...mapActions("payOptions",['add']),
      ...mapActions("orderOptions",['sub'])
    },
    computed: {
      //此时的第一个参数就是声明使用哪个模块
      ...mapState("payOptions",['money']),
      ...mapState("orderOptions",['totalOrder'])
    }
}
</script>
Vue - 点击查看当前系列文章
§ 9、vuex 「 当前文章 」