Vuex

1、理解Vuex

1.1、Vuex是什么?

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

image

1.2.何时使用?

多个组件需要共享数据时

1.3.Vuex工作原理

image

1,具体流程:
插件里调用dispatch方法去找Actions,然后Actions里帮你调用commit找Mutations,然后由Mutations去调用Mutate去找State,然后State修改数据后重新渲染组件。
image

2,就像去餐厅吃饭的流程:客人->服务员->后厨->菜->你
image



2、搭建vuex环境

下载vuex插件:npm i vuex

①创建文件:src/store/index.js

一般会创建一个store文件夹,用来存放vuex的配置项

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state
})

②在main.js中创建vm时传入store配置项

在vue脚手架里,它会扫描代码里所有的import,先执行所有的引入操作,然后再执行其它的代码,包括使用等,所以,在两个import之间存在其它代码也不会打断它会先执行所有improt引入操作。

......
//引入store
import store from './store'
......

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


3、vuex基本使用

①初始化数据、配置actions、配置mutations,操作文件store.js

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)

const actions = {
    //响应组件中加的动作
	jia(context,value){
		// console.log('actions中的jia被调用了',miniStore,value)
		context.commit('JIA',value)
	},
}

const mutations = {
    //执行加
	JIA(state,value){
		// console.log('mutations中的JIA被调用了',state,value)
		state.sum += value
	}
}

//初始化数据
const state = {
   sum:0
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
})

②组件中读取vuex中的数据:$store.state.sum

③组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit



4、求和案例

image

4.1、使用纯vue编写

src\App.vue

<template>
        <div>
            <Count/>          

        </div>  
</template>

<script>
    import Count from "./components/Count.vue"

    export default {
        name:"App",
        components:{ 
            Count            
        },
    }

</script>

src\components\Count.vue

<template>
    
    <div>
        <h1>当前求和为:{{sum}}</h1>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="incremeent">+</button>
        <button @click="decremeent">-</button>
        <button @click="cremeentOdd">奇数再加</button>
        <button @click="incremeentWait">等一等再加</button>
    </div>
  
</template>

<script>

export default{
    name:"Count",
    data(){
        return{
            n:1,//用户选择的数字
            sum:0,//当前的和
        }
    },
    methods:{
        incremeent(){
            this.sum+=this.n;
        },
        decremeent(){
            this.sum-=this.n;
        },
        cremeentOdd(){
            if(this.sum%2){
                this.sum+=this.n;
            }
        },
        incremeentWait(){
            setTimeout(()=>{
                this.sum+=this.n;
            },500);
        }
    }

}
</script>

<style scoped>
    button{
        margin-left:10px;
    }

</style>

4.2、使用纯Vuex编写

搭配好Vuex环境,vuex插件3.0下载命令:npm i vuex@3

src\store\index.js

//该文件用于创建Vuex中最核心的Store

//引入Vuex
import Vue from "vue"

//引入vuex插件
import Vuex from "vuex";

//使用vuex插件
Vue.use(Vuex);


//准备actions,---用于响应组件中的动作
const actions={
    //下面代码没有业务逻辑,直接在外面使用commit调用即可。
    // jia:function(context,value){
    //     //console.log("jia被调用了");
    //     context.commit("JIA",value);
        
    // },
    // jian:function(context,value){
    //     context.commit("JIAN",value);
        
    // },
    jiaOdd(context,value){
        console.log("处理了一些复杂逻辑...");
        //交给jiaOddTest处理
        context.dispatch("jiaOddTest",value);

    },
    jiaOddTest(context,value){
        if(context.state.sum%2){
            context.commit("JIA",value);
        }

    },
    jiaWait(context,value){
         //console.log("jiaWait被调用了");
        setTimeout(() => {
            context.commit("JIA",value);
        }, 500);
    }
}


//准备mutations,---用于操作数据(state)
const mutations={
    JIA(state,value){
        //console.log("mutations中的JIA被调用了");
        state.sum+=value;
    },
    JIAN(state,value){
        //console.log("mutations中的JIA被调用了");
        state.sum-=value;
    },
}


//准备state,---用于存储数据
const state={
    sum:0,//当前的和
}

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

src\App.vue

<template>
        <div>
            <Count/>          

        </div>  
</template>

<script>
    import Count from "./components/Count.vue"

    export default {
        name:"App",
        components:{ 
            Count            
        },
    }

</script>

src\components\Count.vue

<template>
    
    <div>
        <h1>当前求和为:{{$store.state.sum}}</h1>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="incremeent">+</button>
        <button @click="decremeent">-</button>
        <button @click="cremeentOdd">奇数再加</button>
        <button @click="incremeentWait">等一等再加</button>
    </div>
  
</template>

<script>

export default{
    name:"Count",
    data(){
        return{
            n:1,//用户选择的数字
            
        }
    },
    methods:{
        incremeent(){
           this.$store.commit("JIA",this.n);
           
        },
        decremeent(){
            this.$store.commit("JIAN",this.n);
        },
        cremeentOdd(){

            this.$store.dispatch("jiaOdd",this.n);
        },
        incremeentWait(){
            this.$store.dispatch("jiaWait",this.n);
        }
    }

}
</script>

<style scoped>
    button{
        margin-left:10px;
    }
</style>


5、getters配置项

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。相当于全局计算属性。

  2. store.js中追加getters配置

    ......
    
    const getters = {
    	bigSum(state){
    		return state.sum * 10
    	}
    }
    
    //创建并暴露store
    export default new Vuex.Store({
    	......
    	getters
    })
    
  3. 组件中读取数据:$store.getters.bigSum



6、四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

    computed: {
        //借助mapState生成计算属性:sum、school、subject(对象写法)
         ...mapState({sum:'sum',school:'school',subject:'subject'}),
             
        //借助mapState生成计算属性:sum、school、subject(数组写法)
        ...mapState(['sum','school','subject']),
    },
    
  2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
    
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }
    

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



7、多组件共享数据的案例

src\components\Count.vue

<template>
    
    <div>
        <h1>当前求和为:{{he}}</h1>
        <h1>当前求和的10倍为:{{bigSum}}</h1>
        <h3>我在{{xuexiao}},学习{{xueke}}</h3>
        <h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="incremeent(n)">+</button>
        <button @click="decremeent(n)">-</button>
        <button @click="cremeentOdd(n)">奇数再加</button>
        <button @click="incremeentWait(n)">等一等再加</button>
    </div>
  
</template>

<script>

import {mapState,mapGetters,mapMutations,mapActions} from "vuex";

export default{
    name:"Count",
    data(){
        return{
            n:1,//用户选择的数字            
        }
    },
    computed:{
        /* 手动写计算属性
        he(){
            return this.$store.state.sum;
        },
        xuexiao(){
            return this.$store.state.school;
        },
        xueke(){
            return this.$store.state.subject;
        }, */

        //借助mapState生成计算属性,从state中读取数据。(对象写法)
        ...mapState({he:"sum",xuexiao:"school",xueke:"subject",personList:"personList"}),
        

        //----------

        // bigSum(){
        //     return this.$store.getters.bigSum;
        // },

        //借助mapGetters生成计算属性,从getter中读取数据。(对象写法)
        //...mapGetters({bigSum:"bigSum"}),
         ...mapGetters(["bigSum"]),
    },
    methods:{
        /* 手动创建调用commit方法的函数
        incremeent(){
           this.$store.commit("JIA",this.n);
           
        },
        decremeent(){
            this.$store.commit("JIAN",this.n);
        },*/
        
        //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
        ...mapMutations({incremeent:"JIA",decremeent:"JIAN"}),
        //mapMutations方法生成的函数是如下格式,
        /*decremeent(value){
            this.$store.commit("",value);
        },*/


        //----------------

        /*  手动创建
        cremeentOdd(){
            this.$store.dispatch("jiaOdd",this.n);
        },
        incremeentWait(){
            this.$store.dispatch("jiaWait",this.n);
        },
        */

        //借助mapActions生成对应的方法,方法中会调用dispatch去联系action(对象写法)
        ...mapActions({cremeentOdd:"jiaOdd",incremeentWait:"jiaWait"}),


    }

}
</script>

<style scoped>
    button{
        margin-left:10px;
    }
</style>

src\components\Person.vue

<template>
<div>
    <h1>人员组件</h1>
    <h3 style="color:red">Count组件的求和结果为:{{sum}}</h3>
    <input type="text"  placeholder="请输入名字" v-model="name">
    <button @click="add">添加</button>
    <ul>
        <li v-for="p in personList " :key="p.id">{{p.name}}</li>
    </ul>
</div>
  
</template>

<script>

import {nanoid} from "nanoid"

export default{
    name:"Person",
    data(){
        return{
            name:""
        }
    },
    computed:{
        personList(){
            return this.$store.state.personList;
        },
        sum(){
            return this.$store.state.sum;
        }
        
    },
    methods:{
        add(){
            const personObj={id:nanoid(),name:this.name};
            
            this.$store.commit("ADD_Person",personObj);
            this.name="";
        }
    }
}
</script>

src\App.vue


<template>
        <div>
            <Count/> 
            <hr>
            <Person/>         

        </div>  
</template>

<script>
    import Count from "./components/Count.vue"
    import Person from "./components/Person.vue"

    export default {
        name:"App",
        components:{ 
            Count,
            Person            
        },
    }

</script>

src\store\index.js

//该文件用于创建Vuex中最核心的Store

//引入Vuex
import Vue from "vue"

//引入vuex插件
import Vuex from "vuex";

//使用vuex插件
Vue.use(Vuex);


//准备actions,---用于响应组件中的动作
const actions={
    //下面代码没有业务逻辑,直接在外面使用commit调用即可。
    // jia:function(context,value){
    //     //console.log("jia被调用了");
    //     context.commit("JIA",value);
        
    // },
    // jian:function(context,value){
    //     context.commit("JIAN",value);
        
    // },
    jiaOdd(context,value){
        console.log("处理了一些复杂逻辑...");
        //交给jiaOddTest处理
        context.dispatch("jiaOddTest",value);

    },
    jiaOddTest(context,value){
        if(context.state.sum%2){
            context.commit("JIA",value);
        }

    },
    jiaWait(context,value){
         //console.log("jiaWait被调用了");
        setTimeout(() => {
            context.commit("JIA",value);
        }, 500);
    }
}


//准备mutations,---用于操作数据(state)
const mutations={
    JIA(state,value){
        //console.log("mutations中的JIA被调用了");
        state.sum+=value;
    },
    JIAN(state,value){
        //console.log("mutations中的JIA被调用了");
        state.sum-=value;
    },
    ADD_Person(state,value){
        console.log("ADD_Person被调用了");
        state.personList.unshift(value);
    }
}


//准备state,---用于存储数据
const state={
    sum:0,//当前的和
    school:"家里蹲",
    subject:"前端",
    personList:[{id:"001",name:"张三"}]
}

//准备getters,---用于将state中的数据进行加工。
const getters={
    bigSum(state){
        return state.sum*10;
    }

}

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

image



8、模块化+命名空间

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

①修改store.js

为了解决不同模块命名冲突的问题,将不同模块的namespaced: true z之后在不同页面中引入gette r actions mutations时,需要加上所属的模块名。

const countAbout = {
  namespaced:true,//开启命名空间
  state:{x:1},
  mutations: { ... },
  actions: { ... },
  getters: {
    bigSum(state){
       return state.sum * 10
    }
  }
}

const personAbout = {
  namespaced:true,//开启命名空间
  state:{ ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    countAbout,
    personAbout
  }
})

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

//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),

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

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

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

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

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

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

模块化修改上面的多组件共享数据的案例

①在store里建立countOption.js和peresonOption.js
image

②分别将原来src\store\index.js里countOption和peresonOption两个模块的代码打包进相应的js文件并且暴露出来。然后再引用即可。

src\store\countOption.js

//求和相关的配置
export default{
    namespaced:true,//添加命名空间,否则使用vuex提供的四个方法无法识别
    //准备actions,---用于响应组件中的动作
    actions:{
        jiaOdd(context,value){
            console.log("处理了一些复杂逻辑...");
            //交给jiaOddTest处理
            context.dispatch("jiaOddTest",value);

        },
        jiaOddTest(context,value){
            if(context.state.sum%2){
                context.commit("JIA",value);
            }

        },
        jiaWait(context,value){
            //console.log("jiaWait被调用了");
            setTimeout(() => {
                context.commit("JIA",value);
            }, 500);
        }

    },

     //准备mutations,---用于操作数据(state)
    mutations:{
        JIA(state,value){
            //console.log("mutations中的JIA被调用了");
            state.sum+=value;
        },
        JIAN(state,value){
            //console.log("mutations中的JIA被调用了");
            state.sum-=value;
        },
    },

    //准备state,---用于存储数据
    state:{
        sum:0,//当前的和
        school:"家里蹲",
        subject:"前端",
    },

    //准备getters,---用于将state中的数据进行加工。
    getters:{
        bigSum(state){
            return state.sum*10;
        }
    }
}

src\store\peresonOption.js

//人员管理相关的配置

import axios from "axios";

import {nanoid} from "nanoid";

export default{
    namespaced:true,//添加命名空间,否则使用vuex提供的四个方法无法识别

    //准备actions,---用于响应组件中的动作
    actions:{
        //只添加性别王的
        addPersonWang(context,value){
            if(value.name.indexOf('王')===0){
                context.commit("ADD_Person",value);
            }else{
                alert("添加的人必须姓王");
            }
        },
        personServeer(context){
            axios.get("https://api.uixsj.cn/hitokoto/get?type=social").then(response=>{                
                context.commit("ADD_Person",{id:nanoid(),name:response.data});
            },error=>{
                alert(error.message);
            });

        }
    },

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

        ADD_Person(state,value){
            console.log("ADD_Person被调用了");
            state.personList.unshift(value);
        }
    },

    //准备state,---用于存储数据
    state:{
        personList:[{id:"001",name:"张三"}]
    },

    getters:{
        firstPersonName(state){
            //这里的传进来的state就是peresonOption这个里面的局部的state,不是全局总的state
            return state.personList[0].name;
        }
    }

}

src\store\index.js

//该文件用于创建Vuex中最核心的Store

//引入Vuex
import Vue from "vue"

//引入vuex插件
import Vuex from "vuex";

//使用vuex插件
Vue.use(Vuex);


//求和相关的配置
import countOption from "./countOption.js";


//人员管理相关的配置
import peresonOption from "./peresonOption.js"

//创建store并导出
export default new Vuex.Store({
    modules:{
        countAbout:countOption,
        personAbout:peresonOption
    }
})

src\components\Person.vue

<template>
<div>
    <h1>人员组件</h1>
    <h3 style="color:red">Count组件的求和结果为:{{sum}}</h3>
    <h3>列表中第一个人的名字是:{{firstPersonName}}</h3>
    <input type="text"  placeholder="请输入名字" v-model="name">
    <button @click="add">添加</button>
    <button @click="addWang">添加一个姓王的人</button>
    <button @click="addPersonSerever">添加一个随机的人</button>
    <ul>
        <li v-for="p in personList " :key="p.id">{{p.name}}</li>
    </ul>
</div>
  
</template>

<script>

import {nanoid} from "nanoid"

export default{
    name:"Person",
    data(){
        return{
            name:""
        }
    },
    computed:{
        personList(){
            return this.$store.state.personAbout.personList;
        },
        sum(){
            return this.$store.state.countAbout.sum;
        },
        firstPersonName(){
            
           return this.$store.getters["personAbout/firstPersonName"];
        }
        
    },
    methods:{
        add(){
            const personObj={id:nanoid(),name:this.name};            
            this.$store.commit("personAbout/ADD_Person",personObj);
            this.name="";
        },
        addWang(){
             const personObj={id:nanoid(),name:this.name};    
            
             this.$store.dispatch("personAbout/addPersonWang",personObj)
        },
        addPersonSerever(){
            this.$store.dispatch("personAbout/personServeer");
        }
    }
}
</script>

src\components\Count.vue

<template>
    
    <div>
        <h1>当前求和为:{{sum}}</h1>
        <h1>当前求和的10倍为:{{bigSum}}</h1>
        <h3>我在{{school}},学习{{subject}}</h3>
        <h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="incremeent(n)">+</button>
        <button @click="decremeent(n)">-</button>
        <button @click="cremeentOdd(n)">奇数再加</button>
        <button @click="incremeentWait(n)">等一等再加</button>
    </div>
  
</template>

<script>

import {mapState,mapGetters,mapMutations,mapActions} from "vuex";

export default{
    name:"Count",
    data(){
        return{
            n:1,//用户选择的数字            
        }
    },
    computed:{

        //借助mapState生成计算属性,从state中读取数据。
        //...mapState("countAbout",{jia:"sum",xuexiao:"school",kemu:"subject"}), //这样写可以用你定义的键使用值
       ...mapState("countAbout",["sum","school","subject"]), //这样只能通过值名当作键使用

       ...mapState("personAbout",["personList"]),

        //----------

        //借助mapGetters生成计算属性,从getter中读取数据。(对象写法)
         ...mapGetters("countAbout",["bigSum"]),
    },
    methods:{
        
        //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
        ...mapMutations("countAbout",{incremeent:"JIA",decremeent:"JIAN"}),


        //借助mapActions生成对应的方法,方法中会调用dispatch去联系action(对象写法)
        ...mapActions("countAbout",{cremeentOdd:"jiaOdd",incremeentWait:"jiaWait"}),
    }

}
</script>

<style scoped>
    button{
        margin-left:10px;
    }
</style>
posted @ 2022-04-12 17:52  青仙  阅读(49)  评论(0编辑  收藏  举报