0


一文详解Vuex

文章目录

前言

继我的上一篇介绍组件间通信方式的博客之后Vue组件间通信,继续分享一个在vue中实现组件通信的技术——

Vuex

1、

Vuex

概述

Vuex

Vue

团队打造的用于集中式状态管理的一款插件。它能实现多组件之间的数据共享,并且支持多个组件对于状态的处理。所谓的状态,也就是数据。

VueX

致力于集中的管理状态,可以很方便的实现多组件的通信。图示即为Vue官方团队提供的

Vuex

原理图。
在这里插入图片描述
下面来对上方的图示的内容加以描述:

  • Vue Components : 即不同的组件,组件通过派遣不同的行为来练习Actions。事实上,在不涉及什么复杂的业务时候,组件也可以直接与Mutations对话
  • Actions:本质上是一个对象,对象里用于存放实现业务逻辑的方法。注意:在这里一般我们步用来做数据的处理,只用来写业务逻辑。比如,判断条件是否成立,成立的时候再处理数据,比如数据要通过Ajax请求从服务端获取。Actions处理完毕后,把具体的数据处理的任务commitMutationsActions的特点与service层的特点相似。
  • Mutations:本质上是一个对象,存放用来修改数据的函数。由Actions进行业务处理,数据的处理功能由Mutations来实现,类似于dao层。另外,vue的开发者工具就是监视Mutation的变化。
  • State:本质上是一个对象,用于存放共享的数据,最终这些数据会被渲染到组件上。

图中的

actions

mutions

state

最终都要被一个

store

所管理。

store

还能管理的两个配置项为

getters

modules

,之后会介绍到。

2、使用Vuex完成求和案例

Vuex

是一款插件,因此我们就需要安装

Vuex

,引入并且使用这个

Vuex

。值得注意的是,Vue目前的默认版本为3.0,

Vuex

的默认版本为4.0,如果项目是基于Vue2.0的,那么需要引入版本号为3.0的

Vuex

,不然会报错。

安装

Vuex

使用npm包管理工具安装,

npm i vuex@3

引入Vuex

在src目录下新建一个store的文件夹,再新建一个index.js

//该文件用于创建Vuex中最为核心的storeimport Vue from'vue'//引入Vueximport Vuex from'vuex'//应用Vuex插件
Vue.use(Vuex)const actions ={}const mutations ={}const state ={}exportdefaultnewVuex.Store({
    actions,
    mutations,
    state,})

在main.js中引入index.js, 并配置给vue实例

//引入Vueimport Vue from'vue'//引入Appimport App from'./App.vue'//关闭Vue的生产提示
Vue.config.productionTip =falseimport store from'./store'//创建vmnewVue({el:'#app',render:h=>h(App),
    store,})

编写Count组件

<template><div><h1>当前求和为:{{ $store.state.sum }}</h1><selectv-model.number="n"><optionvalue="1">1</option><optionvalue="2">2</option><optionvalue="3">3</option></select><button@click="increment">+</button><button@click="decrement">-</button><button@click="incrementOdd">当前求和为奇数再加</button><button@click="incrementWait">等一等再加</button></div></template>

在这里插入图片描述
我们的目的是通过

Vuex

来管理数据,所以我们应该在

state

里添加

sum
const state ={sum:0}
Vuex

的属性用

$store

获取。

编写回调函数

在上面已经介绍到了,组件将不同的行为派遣给

actions

,因此回调函数里要能够实现这一点,需要用到

$store

下的

dispatch

,可以传递数据。

methods:{increment(){this.$store.dispatch('increment',this.n)},decrement(){this.$store.dispatch('decrement',this.n)},incrementOdd(){this.$store.dispatch('incrementOdd',this.n)},incrementWait(){this.$store.dispatch('incrementWait',this.n)}}

配置actions

const actions ={increment(context, value){
        context.commit('increment', value)},decrement(context, value){
        context.commit('decrement', value)},incrementOdd(context, value){if(context.state.sum %2){
            context.commit('increment', value)}},incrementWait(context, value){setTimeout(()=>{
            context.commit('increment', value)},500)}}

所有的功能实质上只有加和减两种,因此在

mutations

操作加和减即可,这样就实现了业务和数据处理的分离,

actions

专注于业务,

mutations

专注于处理数据。

const mutations ={increment(state, value){
        state.sum += value
    },decrement(state, value){
        state.sum -= value
    },}

这样一个简单的加和案例就做好了。

如果想用上

getters

,可以在加上下面的效果

<h2>放大十倍后:{{$store.getters.bigSum}}</h2>
const getters ={bigSum(state){return state.sum *10}}

然后在store中传入getters即可。
在这里插入图片描述
与计算属性很类似。

总结

基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件store.js``````//引入Vue核心库import Vue from'vue'//引入Vueximport Vuex from'vuex'//引用VuexVue.use(Vuex)const actions ={//响应组件中加的动作increment(context,value){ context.commit('increment',value)},}const mutations ={//执行加increment(state,value){ state.sum += value }}//初始化数据const state ={sum:0}//创建并暴露storeexportdefaultnewVuex.Store({ actions, mutations, state,})
  2. 组件中读取vuex中的数据:$store.state.sum
  3. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)> 备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写> > dispatch> > ,直接编写> > commit>

getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
  2. store.js中追加getters配置......const getters ={bigSum(state){return state.sum *10}}//创建并暴露storeexportdefaultnewVuex.Store({...... getters})
  3. 组件中读取数据:$store.getters.bigSum

3、mapState mapGetters mapMutations mapActions的使用

<h1>当前求和为:{{ $store.state.sum }}</h1><h2>放大十倍后:{{$store.getters.bigSum}}</h2>

我们发现,如果当数据很多,就要写很多

$store. ...

这样的代码,很不方便。我们想要使用数据名直接读取,一种可行的方式就是我们自己配置计算属性,但这也相当麻烦,有什么办法能帮助我们快速的配置计算属性呢?

Vuex

给我们提供了一种方式。

下面我们在多配置几个数据。

const state ={sum:0,school:'清华',subject:'计算机'}
<h1>当前求和为:{{ sum }}</h1><h2>{{ bigSum }}</h2><h3>我在{{ school }},学习{{ subject }}</h3>

接着我们使用

maoState
mapGetters

,首先需要引入。

import{mapState, mapGetters}from'vuex'

配置方式有两种

//借助mapState生成计算属性,从state中读取数据。(对象写法)// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),//借助mapState生成计算属性,从state中读取数据。(数组写法)...mapState(['sum','school','subject']),//借助mapGetters生成计算属性,从getters中读取数据。(对象写法)// ...mapGetters({bigSum:'bigSum'})//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)...mapGetters(['bigSum'])
''

包裹的就是属性名,为了语义化,建议大家还是把计算属性名与state里的属性名配置的一样。
在这里插入图片描述

另外,之前配置methods的时候,如下的代码也出现了很多重复项
在这里插入图片描述
我们也可以解决。

//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)// ...mapMutations({increment: 'increment', decrement: 'decrement'})//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)...mapMutations(['increment','decrement'])//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)// ...mapActions({incrementOdd: 'incrementOdd', incrementWait: 'incrementWait'}),//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)...mapActions(['incrementOdd','incrementWait']),

一般数组写法用的更多。

总结

四个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:'incrementOdd',incrementWait:'incrementWait'})//靠mapActions生成:incrementOdd、incrementWait(数组形式)...mapActions(['incrementOdd','incrementWait'])}
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数methods:{//靠mapActions生成:increment、decrement(对象形式)...mapMutations({increment:'increment',decrement:'decrement'}),//靠mapMutations生成:increment、decrement(数组形式)...mapMutations(['increment','decrement']),}

4、多组件共享数据

前面的例子只有一个组件使用数据,下面这个案例有多个组件共享数据。
在这里插入图片描述

Count

组件结构

<template><div><h1>当前求和为:{{ sum }}</h1><h2>{{ bigSum }}</h2><h3>我在{{ school }},学习{{ subject }}</h3><h3style="color:red">Person组件的总人数是:{{personList.length}}</h3><selectv-model.number="n"><optionvalue="1">1</option><optionvalue="2">2</option><optionvalue="3">3</option></select><button@click="increment(n)">+</button><button@click="decrement(n)">-</button><button@click="incrementOdd(n)">当前求和为奇数再加</button><button@click="incrementWait(n)">等一等再加</button></div></template>
Person

组件结构

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

需要在/store/index.js中添加相应的属性和方法

//mutationsADD_PERSON(state,value){
    console.log('mutations中的ADD_PERSON被调用了')
    state.personList.unshift(value)}//stateconst state ={sum:0,school:'清华',subject:'计算机',personList:[{id:'001',name:'张三'}]}

我们在

Count

组件中使用

map...

生成计算属性和方法,在

Person

组件中使用正常写法。

computed:{...mapState(['sum','school','subject','personList']),...mapGetters(['bigSum'])},methods:{...mapActions(['incrementOdd','incrementWait']),...mapMutations(['increment','decrement'])}
computed:{personList(){returnthis.$store.state.personList
    },sum(){returnthis.$store.state.sum
    }},methods:{add(){const personObj ={id:nanoid(),name:this.name}this.$store.commit('ADD_PERSON',personObj)this.name =''}},

这样写也为了模块化的时候进行比较。
在这里插入图片描述

Count
<template><div><h1>当前求和为:{{ sum }}</h1><h2>{{ bigSum }}</h2><h3>我在{{ school }},学习{{ subject }}</h3><h3style="color:red">Person组件的总人数是:{{personList.length}}</h3><selectv-model.number="n"><optionvalue="1">1</option><optionvalue="2">2</option><optionvalue="3">3</option></select><button@click="increment(n)">+</button><button@click="decrement(n)">-</button><button@click="incrementOdd(n)">当前求和为奇数再加</button><button@click="incrementWait(n)">等一等再加</button></div></template><script>import{mapState, mapGetters, mapActions, mapMutations}from'vuex'exportdefault{name:"Count",data(){return{n:1}},computed:{...mapState(['sum','school','subject','personList']),...mapGetters(['bigSum'])},methods:{...mapActions(['incrementOdd','incrementWait']),...mapMutations(['increment','decrement'])}}</script><stylescoped>button{margin-left: 5px;}</style>
Person
<template><div><h1>人员列表</h1><h3style="color:red">Count组件求和为:{{sum}}</h3><inputtype="text"placeholder="请输入名字"v-model="name"><button@click="add">添加</button><ul><liv-for="p in personList":key="p.id">{{p.name}}</li></ul></div></template><script>import{nanoid}from"nanoid";exportdefault{name:"Person",data(){return{name:''}},computed:{personList(){returnthis.$store.state.personList
    },sum(){returnthis.$store.state.sum
    }},methods:{add(){const personObj ={id:nanoid(),name:this.name}this.$store.commit('ADD_PERSON',personObj)this.name =''}},}</script><stylescoped></style>

4、

Vuex

的模块化

上面介绍了多组件共享数据,但这样做等于把不同组件所用的数据封装在了一起,不好维护。理想的状态是具有相关性的数据都统一放在一个文件夹里,便于后期的维护。比如,上面的数据就可以拆分为关于计数 的,关于人员的。因此我们可以这么分层:
在这里插入图片描述

4.1、页面结构

Count
<template><div><h1>当前求和为:{{ sum }}</h1><h2>{{ bigSum }}</h2><h3>我在{{ school }},学习{{ subject }}</h3><h3style="color:red">Person组件的总人数是:{{personList.length}}</h3><selectv-model.number="n"><optionvalue="1">1</option><optionvalue="2">2</option><optionvalue="3">3</option></select><button@click="increment(n)">+</button><button@click="decrement(n)">-</button><button@click="incrementOdd(n)">当前求和为奇数再加</button><button@click="incrementWait(n)">等一等再加</button></div></template>
Person
<template><div><h1>人员列表</h1><h3style="color:red">Count组件求和为:{{sum}}</h3><h3style="color:red">第一个人:{{firstPersonName}}</h3><inputtype="text"placeholder="请输入名字"v-model="name"><button@click="add">添加</button><button@click="addWang">添加姓王的人</button><button@click="addServer">从服务器中添加</button><ul><liv-for="p in personList":key="p.id">{{p.name}}</li></ul></div></template>

4.2、创建模块

创建并暴露countOptions.js

exportdefault{//开启模块化命名空间namespaced:true,actions:{increment(context, value){
            context.commit('increment', value)},decrement(context, value){
            context.commit('decrement', value)},incrementOdd(context, value){
            context.commit('incrementOdd', value)},incrementWait(context, value){
            context.commit('incrementWait', value)}},mutations:{increment(state, value){
            state.sum += value
        },decrement(state, value){
            state.sum -= value
        },incrementOdd(state, value){if(state.sum %2){
                state.sum += value
            }},incrementWait(state, value){setTimeout(()=>{
                state.sum += value
            },500)},},state:{sum:0,school:'清华',subject:'计算机',},getters:{bigSum(state){return state.sum *10}}}

创建并暴露personOptions.js

import axios from"axios";import{nanoid}from"nanoid";exportdefault{namespaced:true,actions:{addPersonWang(context, value){if(value.name.indexOf('王')===0){
                context.commit('ADD_PERSON', value)}},//添加这么一个功能主要是为了实现一个具体的业务addPersonServer(context){
            axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(response=>{const personObj ={id:nanoid(),name: response.data}
                    context.commit('ADD_PERSON', personObj)},error=>{alert(error.message)})}},mutations:{ADD_PERSON(state, value){
            console.log('mutations中的ADD_PERSON被调用了')
            state.personList.unshift(value)}},state:{personList:[{id:'001',name:'张三'}]},getters:{firstPersonName(state){return state.personList[0].name
        }}}

引入模块

//该文件用于创建Vuex中最为核心的storeimport Vue from'vue'//引入Vueximport Vuex from'vuex'//应用Vuex插件
Vue.use(Vuex)import countOptions from"./countOptions";import personOptions from"./personOptions";exportdefaultnewVuex.Store({modules:{
        countOptions,
        personOptions
    }})

4.3、生成计算属性与方法

Count

组件中,我们使用

map....

,在

Person

组件中,我们自己写。
有两个模块,就相当于有两个store,在取数据和调用方法的时候,我们要注意取自哪个模块的。

Count

组件

computed:{...mapState('countOptions',['sum','school','subject']),...mapState('personOptions',['personList']),...mapGetters('countOptions',['bigSum']),},methods:{...mapActions('countOptions',['incrementOdd','incrementWait']),...mapMutations('countOptions',['increment','decrement'])}

只需要添加模块名即可

Person

组件

与上面那个不同,先看一下

$store

的内容
在这里插入图片描述
在这里插入图片描述

getters与state的组织方式不一样。再往下看
在这里插入图片描述
这样,我们就知道该如何写计算属性和方法了。

computed:{personList(){returnthis.$store.state.personOptions.personList
    },sum(){returnthis.$store.state.countOptions.sum
    },firstPersonName(){returnthis.$store.getters["personOptions/firstPersonName"]}},methods:{add(){const personObj ={id:nanoid(),name:this.name}this.$store.commit('personOptions/ADD_PERSON',personObj)this.name =''},addWang(){const personObj ={id:nanoid(),name:this.name}this.$store.dispatch('personOptions/addPersonWang',personObj)this.name =''},addServer(){this.$store.dispatch('personOptions/addPersonServer')}},

在这里插入图片描述

4.4、模块化+命名空间总结

  1. 目的:让代码更好维护,让多种数据分类更加明确。
  2. 修改store.js``````const countOptions ={namespaced:true,//开启命名空间state:{x:1},mutations:{...},actions:{...},getters:{bigSum(state){return state.sum *10}}}const personOptions ={namespaced:true,//开启命名空间state:{...},mutations:{...},actions:{...}}const store =newVuex.Store({modules:{ countOptions, personOptions }})
  3. 开启命名空间后,组件中读取state数据://方式一:自己直接读取this.$store.state.personOptions.list//方式二:借助mapState读取:...mapState('countOptions',['sum','school','subject']),
  4. 开启命名空间后,组件中读取getters数据://方式一:自己直接读取this.$store.getters['personOptions/firstPersonName']//方式二:借助mapGetters读取:...mapGetters('countOptions',['bigSum'])
  5. 开启命名空间后,组件中调用dispatch//方式一:自己直接dispatchthis.$store.dispatch('personOptions/addPersonWang',person)//方式二:借助mapActions:...mapActions('countAbout',{incrementOdd:'incrementOdd',incrementWait:'incrementWait'})
  6. 开启命名空间后,组件中调用commit//方式一:自己直接committhis.$store.commit('personOptions/ADD_PERSON',person)//方式二:借助mapMutations:...mapMutations('countOptions',{increment:'increment',decrement:'decrement'}),

5、总结

至此,

Vuex

的用法就总结完了,希望能帮到看这篇博客的你。


本文转载自: https://blog.csdn.net/weixin_51624761/article/details/124716183
版权归原作者 理想- 所有, 如有侵权,请联系我们删除。

“一文详解Vuex”的评论:

还没有评论