L o a d i n g . . .
SHIWIVI-文章

//sunny forever
while(life<end){
love++;
beAwesome :)}

    <
  • 主题:
  • + -
  • 清除背景
  • 禁用背景

Vux状态管理

字数:15003 写于:2022-06-13
最新更新:2022-06-13 阅读本文预计花费您43分钟

入门

简介

Vuex是专门为Vue应用程序开发的集中式状态(数据)管理插件,可以对Vue中的组件进行统一的数据管理。

搭建环境

vue2的工程使用vux3,vue3的工程需要使用vux4,版本需要对应,这里以安装vux3为例

在脚手架中执行:

npm i vuex@3

工作流程

vuex工作流程
  • Action主要用于响应Vue Conponents的消息,或者从后端接收数据,并且可以将数据在Action中进行初步处理(主要为异步处理,如:添加定时器)
  • Mutation主要用于处理数据,在Mutation处理的数据才能被Devtools调试工具监测
  • State主要用于集中存储数据

工作流程:Vue Conponents发送消息(dispatch)给Action,或者Action通过Ajax等技术从后端异步获取数据 —> Actions提交(commit)到Mutation —> 数据在Mutation中进行处理后,修改(mutate)State中存储的数据 —> 监测到State数据发生变化,Vue重新渲染(render)页面

vue组件的中数据不要预处理时,也可以越过Action,直接commit到mutation中进行处理

关于store:store是Vuex的核心库,可以理解为一个容器,Action、Mutation、state由Store统一管理,在进行消息提交、数据操作时往往需要经过store,通过this.$store.dispatchthis.$store.commit等语句来调用api

上手

开发步骤

1. 配置store

在src目录下新建store目录,新建index.js文件

import Vue from 'vue'
import Vue from 'vue'
//引入并应用vux
import Vuex from 'vuex'
Vue.use(Vuex)
//创建action、mutations、state
const actions={....}
const mutations={....}
const state={.....}
//创建并暴露Store
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})
2. 引入store配置项

在main.js中引入store配置项

// 全写为import store from './store/index.js'
import store from './store'
new Vue({
  render: h => h(App),
  store
  }).$mount('#app')

案例

eg:简单求和差案例,4个按钮分别实现求和、求差、判断偶数后求和、定时器延时1s求和

  • 直接求和、求差可以直接commit到Mutation中运算
  • 判断当前值是否为偶数,延时求和需要dispatch到Action中预处理,再commit到Mutation中运算
  • 需要多次处理的数据可以在Action中多次dispatch,处理完毕再commit
1. 创建组件

在components中创建Count.vue

<template>
  <div>
    <h2>求和案例</h2>
    <h2>sum经过getters预处理后{{$store.getters.addTen}}</h2>
    <h3>当前值为{{$store.state.sum}}</h3>
    <select v-model.number="num">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>
    <button @click="incrace">加</button>
    <button @click="decrace">减</button>
    <button @click="addOpp">偶数才加</button>
    <button @click="addLate">延迟3s加</button>
  </div>
</template>
<script>
 export default {
    name:'CountSum',
    data(){
        return {
            num:1, //加数
        }
    },
    methods:{
        // 求和、求差直接commit
    incrace(){
        this.$store.commit('JIA',this.num)
    },
    decrace(){
        this.$store.commit('JIAN',this.num)
    },
    //延时、判断奇偶dispatch到actions经过处理后再提交
    addOpp(){
        this.$store.dispatch('addOpp',this.num)
    },
    addLate(){
        this.$store.dispatch('addLate',this.num)
    }
    }
 }
</script>
2. 配置store

新建store目录并在该目录下新建index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions={
    // actions里的方法名一般小写
    //接收参数 (context,实参)
    // context是一个对象,拥有store的部分属性,变量名可以为其他
    addOpp(context,value){          
        if(!(value%2)){             //判断是否为偶数,为偶数则commit到mutations
        context.commit('JIA',value)
        }else{
            console.log('非偶数,数未提交')
        }
    },
    addLate(context,value){       //数据想要多道处理时,可以在actions内多次dispatch,无限套娃
        setTimeout(()=>{
            context.dispatch('addLate2',value);
            console.log('第一次延迟处理')
        },500)
    },
    addLate2(context,value){      //数据经过第二道处理后,再提交
        setTimeout(()=>{
            context.commit('JIA',value);
            console.log('第二次延迟处理')
        },500)
    }
}
// mutations里的方法名一般大写,用于和actions里的区别
const mutations={
    //接收参数(state,实参)
    JIA(state,value){           //加
        state.sum+=value
    },
    JIAN(state,value){          //减
        state.sum-=value;
    }
}
const state={ //存储数据
    sum:0
 }
const getters={                //读取数据前,想要预先进行处理使用getters
    addTen(state){
        return state.sum+10
    }
}
// 创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
 })
3. 引入store配置项

在main.js中引入store配置

import Vue from 'vue'
import App from './App.vue'
import store from './store'// 全写为import store from './store/index.js'
Vue.config.productionTip = false
 new Vue({
  render: h => h(App),
  store
 }).$mount('#app')
4. 引入Count组件

在App中使用Count组件

<template>
  <div id="app">
    <Count/>
  </div>
</template>
<script>
import Count from'./components/Count.vue'
export default {
  name:'App',
  components:{
    Count
             }
   }
</script>

getters

组件从state读取数据前,如果需要对数据进行预处理,可以在getter中进行。如:读取state中的num前进行处理(完整代码位于上一个案例)

1. 在store中添加getter


const actions={.....}
const mutations={....}
const state={.....}
const getter={ 
    //读取state中的sum前,将值增大10
     addTen(state){
        return state.sum+10;
     }
}

2. 读取数据

读取数据使用

$store.getters.addTen

辅助函数

当一个组件需要获取多个数据(状态)时,调用数据和api需要大量使用this.$store.state.number等语句,为了减小代码书写量,可以在计算属性中借助mapStatemapGettersmapMutationsmapActions辅助函数简化代码

通过对象

当组件中的方法名、变量名与State、Mutations…中的变量名不同时,需要通过对象方式接收。并借助模板语法解析变量,以读取State中的数据为例:

...mapState({组件中的变量名:'state数据',.....})
<template>
  <div>
    <h2>求和案例</h2>
    <h2>sum经过getters预处理后{{addTen}}</h2>
    <h3>当前值为{{sum1}}</h3>
    <h2>从state获取name为{{name1}},从state获取song为{{song1}}</h2>
    <select v-model.number="num">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>
    //当使用mapState等语法时,需要在写函数时传参(num)
    <button @click="incrace(num)">加</button>
    <button @click="decrace(num)">减</button>
    <button @click="addOpp(num)">偶数才加</button>
    <button @click="addLate(num)">延迟3s加</button>
  </div>
</template>
<script>
//引入mapState、mapGetters、mapMutations、mapActions
 import {mapState,mapGetters, mapMutations,mapActions} from 'vuex'
 export default {
    name:'CountSum',
    data(){
        return {
            num:1,
        }
    },
    computed:{
        //模板语法
        ...mapState({sum1:'sum',name1:'name',song1:'song'}),
        ...mapGetters({addTen:'addTen'})
        // 可以简写为数组形式...mapGetters(['addTen']),在对象中不能简写,会解析为addTen:addTen,值也解析为变量
    },
    methods:{
    //原语句
    // incrace(){this.$store.commit('JIA',this.num)},
    // decrace(){this.$store.commit('JIAN',this.num)},
    // 借助mapMutations生成对应方法,该方法会自动调用commit,数组写法在另一组件中
     ...mapMutations({incrace:'JIA',decrace:'JIAN'}),
    //原语句
    // addOpp(){this.$store.dispatch('addOpp',this.num)},
    // addLate(){this.$store.dispatch('addLate',this.num)}
     ...mapActions({addOpp:'addOpp',addLate:'addLate'})
     }
 }
</script>

通过数组

当组件中的方法名、变量名与State、Mutations…中的变量名相同时,可以直接使用数组

...mapState(['变量名1','变量名2',....])
<template>
  <div>
    <h2>不同的mapstate等写法</h2>
    <h2>sum经过getters预处理后{{addTen}}</h2>
    <h3>当前值为{{sum}}</h3>
    <h2>从state获取name为{{name}},从state获取song为{{song}}</h2>
    <select v-model.number="num">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>
    <button @click="JIA(num)">加</button>
    <button @click="JIAN(num)">减</button>
    <button @click="addOpp(num)">偶数才加</button>
    <button @click="addLate(num)">延迟3s加</button>
  </div>
</template>
<script>
import {mapState,mapGetters, mapMutations,mapActions} from 'vuex'
export default {
    name:'CountSum',
    data(){
        return {
            num:1,
        }
    },
    computed:{
        ...mapState(['sum','name','song']),
        ...mapGetters(['addTen'])
    },
    methods:{
        //数组写法
    ...mapMutations(['JIA','JIAN']),
     ...mapActions(['addOpp','addLate'])
    }
 }
</script>

模块化与命名空间

当有多类数据需要vuex管理时,可以将他们的state、actions、mutation封装到多个js文件中,并为它们开启命名空间

方法

  1. 在store目录中创建多个store配置文件
  2. 将配置文件统一引入该目录的index.js中
  3. 将store配置引入main.js中
  4. 创建组件,需要注意辅助函数的用法,指向命名空间的方法
  5. 在app中引入组件

案例

eg: 在上个案例基础上加入添加成员的功能。现在有两个功能:求和求差、添加成员,因此store目录下需要两个store配置项。在组件中调用数据时,需要用到命名空间,从不同state中引用数据。

1. 配置store

在store目录新建 person.js文件

//添加人员模块
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
 const person = {
    namespaced:true, //开启命名空间
    actions:{
        addPersonZhou(context,value){
            if(value.name.indexOf('周')===0){
                context.commit('ADD_PERSON',value)
            }
            else{
                alert('只能添加姓周的名')
            }
        },     
    },
    mutations:{
        ADD_PERSON(state,personObj){
            state.personList.unshift(personObj);
        }
    },
    state:{
        personList:[{id:'001',name:'张三'}]
    },
    getters:{
        getFirstName(state){
            return state.personList[0].name;
        }
    }
 }
 export default person

在store目录新建 count.js文件

//求和模块
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default {
    namespaced:true, //开启命名空间
    actions: {
        addOpp(context,value){          //判断是否为偶数
            if(!(value%2)){
            context.commit('JIA',value)
            }else{
                console.log('非偶数,数未提交')
            }
        },
        addLate(context,value){        //数据想要多道处理时,可以dispatch为actions内的其他方法,无限套娃
            setTimeout(()=>{
                context.dispatch('addLate2',value);
                console.log('第一次延迟处理')
            },500)
        },
        addLate2(context,value){      //数据经过第二道处理后,再提交
            setTimeout(()=>{
                context.commit('JIA',value);
                console.log('第二次延迟处理')
            },500)
        }
    },
    mutations:{
        JIA(state,value){           //加
            state.sum+=value
        },
        JIAN(state,value){          //减
            state.sum-=value;
        },
    },
    state:{
        sum:0,
        name:'周杰伦',
        song:'夜曲',
    },
    getters:{
        addTen(state){
            return state.sum+10
        }
    }
}
2. 在index.js中引入

在store目录下index.js中引入

 import Vue from 'vue'
 import Vuex from 'vuex'
Vue.use(Vuex)
//引入两个配置项
import count from './count'
import person from './person'
// 创建并暴露store
export default new Vuex.Store({
    //引入模块
    modules:{
        //全写count: count,
        count,
        person
    }
 })

3. 在main.js中引入store

import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
  render: h => h(App),
  store
}).$mount('#app')
4. 书写组件

创建count.vue组件


<template>
  <div>
    <h2>求和案例</h2>
    <h2>sum经过getters预处理后{{addTen}}</h2>
    <h3>当前值为{{sum}}</h3>
    <h2>从state获取name为{{name}},从state获取song为{{song}}</h2>
    
    <select v-model.number="num">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>
    <button @click="incrace(num)">加</button>
    <button @click="decrace(num)">减</button>
    <button @click="addOpp(num)">偶数才加</button>
    <button @click="addLate(num)">延迟3s加</button>
    <h1>组件2共享过来的人员名单</h1>
   <ol class="person-list">
    <li v-for="person in personList" :key="person.id">{{person.name}}</li>
    </ol>
  </div>
</template>
<script>
 import {mapState,mapGetters, mapMutations,mapActions} from 'vuex'
 export default {
    name:'CountSum',
    data(){
        return {
            num:1,
        }
    },
    //使用mapState,mapGetters, mapMutations,mapActions时更简洁
    computed:{
        // 从count组件获取数据
        ...mapState('count',{sum:'sum',name:'name',song:'song'}),
         // 从person组件获取数据
        ...mapState('person',['personList']),

        ...mapGetters('count',{addTen:'addTen'})
    },
    methods:{
    ...mapMutations('count',{incrace:'JIA',decrace:'JIAN'}),
    ...mapActions('count',{addOpp:'addOpp',addLate:'addLate'})
    }
 }
</script>
辅助函数指向命名空间时,需要在数据和方法名前添加命名空间名,如:...mapState('命名空间',{参数})

person.vue组件

<template>
  <div>
    <h2>组件2:添加成员</h2>
    <input type="text" v-model="personName" placeholder="添加成员">
    <button @click="add">添加</button>
    <button @click="addZhou">只添加姓周的人</button>
    <ul>
        <li v-for="person in personList" :key="person.id">{{person.name}}</li>
    </ul>
    <h2>名单第一个人为{{firstPersonName}}</h2>
    <h2>组件一共享的值</h2>
    <h3>和为:{{sum}}</h3>
  </div>
</template>
<script>
import { nanoid } from 'nanoid'
export default {
    name:'CountSum',
    data(){
        return {
            personName:''
        }
    },
    //不使用mapState,mapGetters, mapMutations,mapActions时更新、获取数据的方法
    computed:{
        personList(){
            return this.$store.state.person.personList
        },
        sum(){
            return this.$store.state.count.sum
        },
        //通过getters获取时,想要指定组件名与路径
        firstPersonName(){
            return this.$store.getters['person/getFirstName']
        }
    },
    methods:{
     add(){
        const personObj={id:nanoid(),name:this.personName};
        //指定为person组件下的ADD_PERSON函数
        this.$store.commit('person/ADD_PERSON',personObj);
        this.personName='';
     },
     addZhou(){
         const personObj={id:nanoid(),name:this.personName};
         this.$store.dispatch('person/addPersonZhou',personObj);
         this.personName='';
     }
    }
  }
</script>
不借助mapState等辅助函数,需要使用路径来指定命名空间名称,其中读取state中的数据使用this.$store.state.配置文件名.数据名来指向命名空间,而dispatchcommitgetter等api需要使用this.$store.api名['命名空间/方法名',变量名]语句来指向命名空间
5. App.vue引入两个组件即可
<template>
  <div id="app">
    <Count/>
    <Person/>
  </div>
</template>
 <script>
 import Count from'./components/Count.vue'
 import Person from'./components/Person.vue'
 export default {
  name:'App',
  components:{
    Count,
    Person
  }
 }
</script>
上一篇:Grid栅格布局
下一篇:一位资深数据工程师酒后的肺腑之言(转载)
z z z z z