vue-----vuex的使用

94 阅读6分钟

vuex的使用

vuex是一种集中式管理状态的工具,他也是组件间通信的一种方式,他可以更加方便的在组件间共享数据。

vuex的原理图

vuex.jpg

vuex原理

在图中我们可以看到,红色方框指的是store,他里面存放了vuex最重要的三个东西,分别是:Actions,Mutations,State。他们都是对象,里面进行相关配置。

State存放着我们要共享的数据,也就是说我们将要共享的数据放进去,在组件中就不用写了。 我们在组件中使用dispatch方法向actions传递要进行的操作与数据,Actions一般会进行一些业务操作,之后使用commit传递给Mutations,Mutations对state中的数据今年操作,时候重新渲染页面。这就是一个大概的流程,具体流程我们在例子中体现。

打一个比方,组件是来饭店吃饭的客人,他点菜提交给服务员(Actions),之后服务员(Actions)将客人点的菜交给后厨(Mutations),后厨做菜(对数据进行操作)。

vuex的基本流程

一、配置vuex

1、下载vuex 在这里我使用的是vuex3版本

npm i vuex@3

2、配置store 在图中我们可以看到store存放着vuex的相关配置,所以我们要配置store 在src目录中新建一个文件夹:store,在里面建立index.js,书写相关配置

//引入vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//使用vuex
Vue.use(Vuex)

//创建actions
const actions={

}

//创建mutations
const mutations={

}

//创建state
const state={

}

//创建store并暴露,将相关配置放入其中
export default new Vuex.Store(
    {
        actions,
        mutations,
        state
    }
)

在创建好store后,我们将他配置在main.js中,这是为了能让所有组件都能使用store.

import Vue from 'vue'
import App from './App.vue'
//引入store
import store from './store/index'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store,//配置store
  beforeCreate(){
    Vue.prototype.$bus=this//安装全局总线
  }
}).$mount('#app')

这样子我们就可以使用vuex了。

二、一个例子

具体的流程我们在例子中体现。 我们有一个组件CountDemo,他可以实现,+ ,-, 和为奇数时再加, 等一秒再加四个功能。

capture_20221004103441042.bmp

为了简单说明vue的基本使用我们就先创建这一个组件(当然还有App组件)。 我们先不用vuex实现这四个功能。

<template>
  <div>
    <h2>当前求和为:{{sum}}</h2>
    <select v-model.number="n">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>

    <button @click='increment'>+</button>
    <button @click='decrement'>-</button>
    <button @click='oddIncrement'>奇数加</button>
    <button @click='waitIncrement'>等一秒加</button>
  </div>
</template>

<script>
export default {
    name:'countDemo',
    data(){
        return{
            sum:0,//求和
            n:1//选择框的数据
        }
    },
    methods:{
        increment(){
            this.sum+=this.n
        },
        decrement(){
            this.sum-=this.n
        },
        oddIncrement(){
            if(this.sum%2!==0){
                this.sum+=this.n
            }
        },
        waitIncrement(){
            setTimeout(()=>{
                this.sum+=this.n
            },1000)
        }

    }
}
</script>

<style>
    button{
        margin: 5px;
    }
</style>

还是很简单的,现在我们将sum交给vuex管理,所以在组件中就不写sum了

data(){
        return{
            n:1//选择框的数据
        }
    },

将他交给store中的states

const state={
    sum:0
}

接下来,就是客人(组件)要点菜了,使用dispatch这个方法。我们先完成加法这个功能。

由于将sum交给了store中的state,所以在组件中我们就不能使用原来的方式来进行加法了,就先把原来increment等方法的内容删掉吧.

在CountDemo组件中,increment方法如下,在dispatch方法中我们要传入两个参数,第一个参数是,进行的操作名,有点类似于消息订阅与发布的那个消息名,第二个参数为我们传入的数据。

increment(){
            this.$store.dispatch('jia',this.n)
        },

这样客人(组件)就点好了菜,接下来就是服务员(actions)接收点的菜,并将菜传递给厨师(mutations).

在store中,action接收组件传递过来的操作与数据

const actions={
    jia(context,value){
        context.commit('JIA',value)
    }
}

我们看到actions中存放的就是一些方法,方法名就是我们在组件中传递过来的操作名。方法有两个参数:context与value。value就是我们传递过来的数据。那么context是什么呢?

context.bmp 我们将context输出发现,他其实就是一个对象,里面有我们需要的一些东西,就比如说commit。使用

context.commit('JIA',value)

就可以将点的菜传递给厨师(mutations)。让厨师进行操作。

接下来就是厨师(mutations)做菜的环节了.

const mutations={
    JIA(state,value){
        state.sum+=value
    }
}

我们发现有一个熟悉的身影但是又不太熟悉的东西,那就是‘JIA’,其实这就是那个我们传递过来的操作,在actions中用来大写其实就是为了区分一下,显得他比较重要,没啥特殊的意思。所以在mutations中也用大写。

mutation中也是一些方法,方法也有两个参数,state与vaule,这两个我们就比较熟悉了,state就是就是我们存放共享数据的地方,mutations是操作数据的地方所以拿到他,合乎情理。value就是我们传递过来的数据。 这样一整套vuex的流程就走完了,我们的加法功能也可以实现了。

接下来我们实现减法的功能:减法与加法基本是一致的。唯一的不同就是mutations操作数据是减法。我们重新捋一遍。

首先客人(组件)点菜

decrement(){
            this.$store.dispatch('jian',this.n)
        },

之后服务员(actions)将点的菜传递给厨师(mutations)

const actions={
    jia(context,value){
        context.commit('JIA',value)
    },
    jian(context,value){
        context.commit('JIAN',value)
    }
}

厨师(mutations)开始做菜

const mutations={
    JIA(state,value){
        state.sum+=value
    },
    JIAN(state,value){
        state.sum-=value
    }
}

这样减法功能也实现了。

三、带有业务逻辑的操作

现在我们实现第三个功能,当和为奇数时才能加。我们肯定明白首先要判断当前的和是否为奇数,才能决定是否进行进行加法计算。但是在哪里进行判断呢?其实从理论上来讲在组件中,在actions,在mutations中都可以取到sum的值,都可以进行判断,但是呢,我之前说过,一般在actions中会进行一些业务逻辑,比如判断,循环,发送ajax请求,所以我们在actions中进行判断。

老规矩先点菜

oddIncrement(){
            this.$store.dispatch('oddIncrement',this.n)
        },

之后在actions中判断,并且传递给mutations。

oddIncrement(context,value){
        console.log(context);
        if(context.state.sum%2!==0){
            context.commit('JIA',value)
        }
    }

我们看到,在这里我并没有在写一个mutations操作,直接判断通过后,进行'JIA'的操作就好了。这样当和为奇数时再加,就完成了。

接下来类似我们在完成等一秒再加的操作,与和为奇数时再加。

点菜

waitIncrement(){
            this.$store.dispatch('waitIncrement',this.n)
        }

传菜

waitIncrement(context,value){
        setTimeout(()=>{
            context.commit('JIA',value)
        },1000)
    }

这样等一秒的操作就完成了。这就是vuex的基本操作流程。

优化一

在上个例子中我们发现,加法与减法的actions其实并没有业务逻辑,这种情况下,其实我们可以在组件中使用commit直接向mutations传递操作与参数,并不用在actions写东西了。

组件中

increment(){
    this.$store.commit('JIA',this.n)
        },
decrement(){
    this.$store.commit('JIAN',this.n)
    },

在acctions中就不写我们当时写的 jia jian了。

优化二

现在我们增加一下需求,要在组件中新显示网站名字,与语言两项。并且这两个属性要交给 vuex保管。在store中的index.js中添加两个属性

const state={
    sum:0,
    webName:'稀土掘金',
    language:'vuex'
}

在组件中显示

<h2>网站名字:{{$store.state.webName}}---语言:{{$store.state.language}}</h2>

效果

j1.png 在组件中我们发现需要频繁的使用$store.state去读取state中的数据。这样不太符合vue的风格,所以为了解决这个问题。我们引入一个api---mapState。他可以帮助我们更方便的读取state中的数据。我们需要在computed中应用这个api。当然了需要引入在使用。

computed:{
        ...mapState({sum:'sum',webName:'webName',language:'language'})
    }

这样我们就很方便的读取出来了state中的数据,在组件中就可以很方便的使用。

<h2>当前求和为:{{sum}}</h2>
<h2>网站名字:{{webName}}---语言:{{$store.state.language}}</h2>

mapState还有第二种写法

 ...mapState(['sum','webName','language'])

优化三

在之前组件的methods中,我们要点菜们也就是向actions中传递方法与参数(使用dispatch),要自己写如下代码

oddIncrement(){
           this.$store.dispatch('oddIncrement',this.n)
        },
waitIncrement(){
    this.$store.dispatch('waitIncrement',this.n)
}

如果操作比较多的话,自己写就太烦了,为了解决这个问题,我们使用mapActions。可以帮助生产上面的代码。同样在使用前我们要引入。在methods中。

...mapActions({oddIncrement:'oddIncrement',waitIncrement:'waitIncrement'})

这样就会帮我们实现上面我们自己写的代码。还有一个问题,我们是需要传递参数的,参数怎么传递呢?在mapActions中我们并没有传递参数。这时就需要在结构中定义方法时传递参数。

<button @click='oddIncrement(n)'>奇数加</button>
<button @click='waitIncrement(n)'>等一秒加</button>

优化四

我们来回顾一下,我们们优化后的代码是什么样子

<template>
  <div>
    <h2>当前求和为:{{sum}}</h2>
    <select v-model.number="n">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>

    <button @click='increment'>+</button>
    <button @click='decrement'>-</button>
    <button @click='oddIncrement(n)'>奇数加</button>
    <button @click='waitIncrement(n)'>等一秒加</button>

    <h2>网站名字:{{webName}}---语言:{{language}}</h2>
    
  </div>
</template>

<script>
import {mapState,mapActions} from 'vuex'
export default {
    name:'countDemo',
    data(){
        return{
            n:1
        }
    },
    methods:{
        increment(){
            this.$store.commit('JIA',this.n)
        },
        decrement(){
            this.$store.commit('JIAN',this.n)
        },
        
        ...mapActions({oddIncrement:'oddIncrement',waitIncrement:'waitIncrement'})

    },
    computed:{
        ...mapState(['sum','webName','language'])
    }
}
</script>

<style>
    button{
        margin: 5px;
    }
</style>

目前为止我们优化了读取state,优化了向actions中传递方法。

现在我们优化这几行代码

increment(){
         this.$store.commit('JIA',this.n)
        },
decrement(){
    this.$store.commit('JIAN',this.n)
},

这几句代码是直接向mutations中通信。如何优化呢?我们使用mapMutations,在使用前也需要引入

 ...mapMutations({increment:'JIA',decrement:'JIAN'}),

他同样帮助我们生成上面的代码。记得在结构中传参,这一点和mapStatas相同。 来看看我们最终优化的代码吧

<template>
  <div>
    <h2>当前求和为:{{sum}}</h2>
    <select v-model.number="n">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>

    <button @click='increment(n)'>+</button>
    <button @click='decrement(n)'>-</button>
    <button @click='oddIncrement(n)'>奇数加</button>
    <button @click='waitIncrement(n)'>等一秒加</button>

    <h2>网站名字:{{webName}}---语言:{{language}}</h2>
    
  </div>
</template>

<script>
import {mapState,mapActions,mapMutations} from 'vuex'
export default {
    name:'countDemo',
    data(){
        return{
            n:1
        }
    },
    methods:{
  
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        ...mapActions({oddIncrement:'oddIncrement',waitIncrement:'waitIncrement'})

    },
    computed:{
        ...mapState(['sum','webName','language'])
    }
}
</script>

<style>
    button{
        margin: 5px;
    }
</style>

是不简单了好多。

组件间共享数据

vuex本来就是为了方便管理数据与方便组件间通信的。现在我们来实现组件间共享数据。

我们再来写一个组件PersonList,用他来展示一些人的数据。并且数据保存在vuex中。

const state={
    sum:0,
    webName:'稀土掘金',
    language:'vuex',
    persons:[
        {name:'张三',age:18},
        {name:'李四',age:19}
    ]
}

下面是PersonList组件

<template>
 <div>
    <h2>PersonList组件</h2>
  <ul>
    <li v-for='p in $store.state.persons' :key=p.index>{{p.name}}--{{p.age}}</li>
  </ul>
</div>
</template>

<script>
export default {
    name:'PersonList'
}
</script>

<style>

</style>

在页面的效果,(记得在App组件中呈现,这里就不写了)

ppp.png

接下来我们实现两个功能,添加人员与显示上方组件的求和信息。

<template>
<div>
    <h2>PersonList组件</h2>
    <input type="text" placeholder="请输入名字" v-model="name">
    <input type="text" placeholder="请输入年龄" v-model="age">
    <button @click='add'>提交</button>
  <ul>
    <li v-for='p in persons' :key=p.index>{{p.name}}--{{p.age}}</li>
  </ul>

  <h2>上方组件的求和为:{{sum}}</h2>
</div>
  
</template>

<script>
import {mapState} from 'vuex'
export default {
    name:'PersonList',
    data(){
        return{
            name:'',
            age:0
        }   
    },
    methods:{
        add(){
            const personObj={name:this.name,age:this.age}
            this.$store.commit('addPerson',personObj)
        }
    },
    computed:{
        ...mapState(['sum','persons'])
    }
}
</script>

<style>

</style>

实现啦

qw.png

我是小白,如有问题欢迎大家指出错误。