Vuex保姆级讲解和具体使用

官方定义

Vuex是一个专为vue.js应用程序开发的状态管理模式。采用集中式存储管理应用的所有组件的状态,
并以相应的规则保证状态以一种可预测的方式发生变化。

说人话

1、Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

vuex是为vue.js开发的一个插件(包),这个插件(包)就是用来做状态管理的。
状态管理就是数据管理,也就是,vuex就是去管理vue中的数据的。

2、采用集中式存储管理应用的所有组件的状态。

vue是组件化开发,把一个页面拆分成多个小块。每小块都有自己的数据来呈现。
比如下拉框有下拉框供选择的数据,表格有表格要呈现的数据。
那么这些数据可以直接放在.vue文件中的data里面去管理,
但是如果是大项目的话,数据放在.vue中的data去管理略有欠缺。
所以:可以使用vuex去统一存放管理各个组件的数据。
vuex就像一个仓库,用来存放组件中需要用到的数据,
至于管理,就是增删改查,往vuex中存取、修改、删除等操作

3、以相应的规则保证状态以一种可预测的方式发生变化

这句话的意思就是,想要存取、修改、删除vuex仓库中的状态数据, 需要按照一定的语法规则,
比如按照action-->mutaion-->state的规则去增删改查,
比如使用辅助函数如增删改查vuex中的数据。
这个具体的规则下文中的vuex使用步骤中会逐一讲解。

所以vuex就是一个仓库,用来存放数据的。所以我们使用vuex一般会新建一个store文件夹,store单词的中文意思就是商店、仓库的意思

应用场景

  • 正常数据放到data里面就行了,免得麻烦,一般小项目都很少用到vuex,毕竟vuex的步骤稍微多一点
  • 一般公共数据放到vuex中会比较好,毕竟组件有自己的data可以存放数据,公共的变量如用户信息、token等。vuex存一份,localstorage再存一份,取数据直接从vuex里面取,取不到再从localstorage里面去取。
  • 跨很多组件层级的数据通信,也可以通过vuex去做管理,毕竟vuex就像一个对象一个,好多组件都指向这个对象,当这个vuex对象发生了变化,所有的组件中对应的内容都会发生变化,这样就能做到实时响应,一个变,大家都变。

使用步骤

首先要搭建好项目,搭建项目的过程不赘述,项目搭建好了,我们就可以按照如下步骤使用vuex了

1、npm下载安装vuex插件

因为vuex是特定的用来管理vue中的数据的一款插件,所以按照可插拔框架的思想,想要使用vuex就下载安装,不想用的时候就卸载即可

npm install vuex --save

2、新建store文件夹注册使用vuex插件

image.png

把store对象挂载到vue对象上面的话,那么每个组件都可以访问到这个store对象,那么每个组件都能去使用vuex

vue实例如下

image.png

$store的上具体内容

image.png

vue的总实例上挂载的vuex的store对象中有定义的state、mutations、actions、getters,那么我们可以通过 this.store...就可以在各个组件上访问和使用vuex中的数据。这就验证了vuex文档中的那句:
vuex采用集中式存储管理应用的所有组件的状态 都集中在vue实例上了,所有组件的状态确实都可以访问到了。

其实学习vuex就是学习两点:
1、如何读取vuex中仓库的数据
2、如何修改vuex中仓库的数据

3、读取vuex中仓库的数据
我们在vuex中的state里面定义一个属性

export default new Vue.Store({
    state:{
        msg: '我是vuex'
    },
    ...
})

接下来在组件中使用这个数据

方式一 双括号表达式直接使用(不推荐)

<h2>{{this.$store.state.msg}}</h2>

方式一不太优雅,一般不用,主要使用方式二或方式三

方式二 mounted中去取用vuex中的数据

<template>
  <div class="box">
      <h2>{{msg}}</h2>
  </div>
</template>
<script>
export default {
    data() {
        return { msg:'' }
    },
    mounted() {
        this.msg = this.$store.state.msg
    },
}
</script>

方式三 使用computed去取vuex中的数据

<template>
  <div class="box">
      <h2>{{msg}}</h2>
  </div>
</template>
<script>
export default {
    computed: {
        msg(){ return this.$store.state.msg }
    }
}
</script>

4、修改vuex中的数据

一般是在事件的回调函数中去修改vuex中的数据,比如我们点击一个按钮,去修改vuex中的数据

方式一 直接修改(不推荐)

<template>
  <div class="box">
      <h2>{{msg}}</h2>
      <el-button @click="changeVuex">修改</el-button>
  </div>
</template>

<script>
export default {
    methods: {
        // 直接赋值修改vuex中的state的数据
        changeVuex(){
            this.$store.state.msg = '修改vuex'
        },
    },
    computed: {
        msg(){ return this.$store.state.msg }
    }
}
</script>

这种方式勉强能用,不过vuex当开启了严格模式的时候,就会报错!!!开启严格模式代码如下:

export default new Vuex.Store({
    strict:true, // 开启严格模式
    state:{
        msg:'我是vuex哦'
    },
    // 等...
})

报错信息:Error:[vuex] do not mutate vuex store state outside mutation handlers.

不要不通过mutation的操作就去修改vuex中store里面的state状态值

所以由此我们就想到了vuex定义的那句话:并以相应的规则保证状态以一种可预测的方式发生变化这里的相应的规则就是指,
想要修改vuex中的数据,就要按照vuex中操作数据的步骤流程规则来,要不然就给你报错。
那么vuex定义的修改state的规则是什么呢?如下图

方式二 action-->mutation-->state 官方图解如下

image.png

看完上图以后,我们可以总结vuex的使用规则如下

  • 组件想要去更改vuex中的数据,但是组件自己只是口头传唤一下actions干活,即:dispatch一下actions
    (组件说:嘿,actions,我要更改vuex中的数据了,你发个请求,从后端接口中拿到我要的数据去更改一下)
  • action得到消息后,就会向后端发请求,获取到后端返回的数据,action拿到后端返回的数据以后,就把数据commit提交给mutations,即:commit一下mutations
    (actions拿到数据以后,但是也比较懒,把数据交给仓库管理员mutations,告知要更改对应数据以后,就撤了)
  • mutations相当于最终的仓库管理员,由这个仓库管理员去修改vuex仓库中的数据,即:mutate一下state
    (mutations任劳任怨,就去更改vuex中state的数据,更改完以后,就等待下一次的干活,mutations修改数据的过程,会被仓库的监控,也就是vue的开发工具devTool记录下来)
  • 而vue数据是响应式的,仓库数据一改变,对应使用仓库数据的组件就会重新render渲染,所以页面效果也就改变了

组件中如果不是异步发请求去更改数据,也可以直接跳过actions,直接让仓库管理员mutations去修改数据,不过这种方式不是太多

组件代码如下

<template>
  <div class="box">
      <h2>{{msg}}</h2>
      <el-button @click="changeVuex">修改</el-button>
  </div>
</template>

<script>
export default {
    methods: {
        changeVuex(){
            this.$store.dispatch('actionsChange')
        },
    },
    computed: {
        msg(){ return this.$store.state.msg }
    }
}
</script>

vuex代码如下

export default new Vuex.Store({
    strict:true,
    state:{
        msg:'我是vuex哦'
    },
    mutations:{
        // 这里第一个形参state就是仓库state,是可以访问到state里的msg的值,即 可以修改state
        // 第二个形参params是actions中传过来的数据
        mutationsChange(state,params){
            console.log(state,params);
            state.msg = params
        }
    },
    actions:{
        // 这里的形参store对象下面有commit方法
        // 可以去告知对应的mutations中的函数执行
        actionsChange(store){
            console.log(store);
            setTimeout(() => {
                store.commit('mutationsChange', '规范修改vuex')
            }, 500);
        }
    }
})

补充getter加工

getter中我们可以定义一个函数,这个函数我们用来修改state中的值的,函数接收一个参数state,
这个state参数就是当前的state对象,通过这个参数可以加工state中的数据,加工好return出去,以供组件中使用。

// vuex
export default new Vuex.Store({
    strict:true,
    state:{
        msg:'我是vuex哦'
    },
    getters:{
        gettersChange(state){
            return state.msg + '---getter修改state中的数据'
        }
    },
})

组件中使用的时候,就直接使用getter中的数据即可,如下:

this.$store.getters.gettersChange

当然也可以不用getter,就是在组件中取到vuex中的数据以后,我们再进行加工。不过能在getter中加工的最好就在getter中加工,因为这样写代码,比较优雅

辅助函数

函数函数的出现,就是为了让我们能少写几行代码,我们以获取vuex中state为例,
假设我们在一个组件中需要获取多个state中的值,这样的话这个语句就要写多次,
this.store.state.msg1、this.store.state.msg2、this.$store.state.msg3等。
为了简化,vuex内部封装了四个辅助函数,分别用来对应state,mutations,actions,getters的操作。

辅助函数,简而言之,就是尤大佬封装的语法糖

辅助函数一般搭配计算属性和方法使用

mapState辅助函数

第一步,假设vuex仓库中有三个数据,我们需要在组件上使用这三个数据

// store.js
export default new Vuex.Store({
    state:{
            msg1:'辅助函数一',
            msg2:'辅助函数二',
            msg3:'辅助函数三',
          },
}

第二步,从vuex插件中引入辅助函数

import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'

第三步,在计算属性中使用辅助函数mapstate取到state中的数据

// 方式一,数组形式
computed: {
        ...mapState(['msg1','msg2','msg3'])
},
    
// 方式二, 对象形式(vuex模块化比较常用)
computed: {
    ...mapState({
        msg1: state=>state.msg1,
        msg2: state=>state.msg2,
        msg3: state=>state.msg3,
    })
}, 

第四步,在组件中直接就可以在差值表达式中使用了

<template>
    <div>
        <h1>{{msg1}}</h1>
        <h2>{{msg2}}</h2>
        <h3>{{msg3}}</h3>
    </div>
</template>
mapGetters辅助函数

使用方法和mapState基本一样

computed:{
  ...mapGetters(['msg']),
}
mapMutations辅助函数

比如,我们在按钮点击事件的回调函数中去触发mutations,对比一下,不用辅助函数和用辅助函数的语法书写区别

vuex结构

mutations:{
    kkk(state,params){
        state.msg = params
    }
},

html结构

<template>
  <div>
    <h2>{{ msg }}</h2>
    <el-button @click="kkk('我是参数')">辅助函数mapActions</el-button>
  </div>
</template>

js代码

<script>
import { mapState, mapMutations } from "vuex";
export default {
  computed: {
    ...mapState(["msg"]),
  },
  methods: {
    // 不使用辅助函数的写法
    kkk(params) {
      this.$store.commit("kkk", params);
    },
      
    // 使用辅助函数的写法
    ...mapMutations(["kkk"]),
  },
};
</script>

注意:使用辅助函数,貌似没有地方传参,实际是辅助函数帮我们默默的传递过去了,
这个参数需要写在html结构中的点击语句中,
如上述代码:<el-button @click="kkk('我是参数')">辅助函数mapActions</el-button>

mapActions辅助函数

mapActions的用法和mapMutations的用法基本上一样,就换个单词即可,在此不赘述

...mapActions(["sss"])

意思是:去触发Actions中的sss函数

vuex的module模块化

提起模块化,思想还是那句话,大而化小,便于管理。很多语言都有模块化的应用,vuex也是一样。
试想,如果所有的状态数据都写在一起,看着容易眼花缭乱,不便于管理。
所以尤大老对于vuex的设计中,就做了模块化module的处理

图解步骤
[图片上传失败...(image-5b8c1b-1715586876333)]
在组件中使用vuex模块化

获取vuex模块中的数据

这里以获取state中的数据为例,获取getters中的数据写法基本一样

<template>
  <div>
    <h2>{{ msg }}</h2>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: "CodeVue",
  computed: {
    // 正常方式
    msg(){
      return this.$store.state.vue.module// 找state里的vue模块下的module的值
    },
      
    // 使用辅助函数方式,这里用对象的写法
    ...mapState({
      msg:state=>state.vue.module// 找state里的vue模块下的module的值
    })
  }
};
</script>

修改vuex模块中的数据

这里以触发mutations为例,actions写法基本一致

  • 不使用辅助函数
<template>
  <div>
    <h2>{{ msg }}</h2>
    <el-button @click="moduleChange">模块化修改值</el-button>
  </div>
</template>

<script>
export default {
  name: "CodeVue",
  computed: {
    ...mapState({
      msg:state=>state.vue.module
    })
  },
  methods: {
    moduleChange(){
      // 注意,直接提交对应模块的方法即可,commit会自动找到对应vuex下的方法
      this.$store.commit('moduleChange','我是参数')
    }
  },
};
</script>
  • 使用辅助函数
<template>
  <div>
    <h2>{{ msg }}</h2>
    <!-- 我们在点击事件的语句中,把data中定义的参数带过去,去提交mutations -->
    <el-button @click="moduleChange(canshu)">模块化修改值</el-button>
  </div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
export default {
  name: "CodeVue",
  data() {
    return {
      canshu:'我是参数'
    }
  },
  computed: {
    ...mapState({
      msg:state=>state.vue.module
    })
  },
  methods: {
    ...mapMutations(['moduleChange'])
  },
};
</script>

注意,上述我使用vuex的模块化module的时候,没有加上命名空间namespace,
所以去提交对应模块下的mutations的时候,可以直接写this.$store.commit('moduleChange','我是参数')或...mapMutations(['moduleChange'])这样的话,
vuex会去自己所有模块下去找moduleChange这个函数,然后去修改。
这样的话,略微浪费性能,因为,默认情况下,vuex模块内部的 action、mutation 和 getter 是挂载注册在全局命名空间的,这样使得多个模块能够对同一 mutation 或 action去操作,就不停的找,直到找到为止。
但是一般情况下,我们使用vuex模块化的时候都会加上命名空间,做到独立、复用。
接下来我们说一下,vuex模块化的标准用法,即加上命名空间的用法。
命名空间

  • 不加命名空间,所有的都找一遍。
  • 加了的话,只去特定的模块找。
  • 所以使用命名空间的话,提交mutations写法就变了。

写法如下:

// 不使用辅助函数
moduleChange(){
    this.$store.commit('vue/moduleChange'); // 以斜杠分割,斜杠前写对应模块名,斜杠后写对应mutations中的方法名
}
    
// 使用辅助函数
...mapMutations('vue',['moduleChange']) // 以逗号分割,逗号前写模块名,逗号后是个数组,数组中放置对应mutations中的方法名

//3.别名状态下
...mapMutations({
    anotherName:'vue/moduleChange' // 和不使用辅助函数一样
}),

总结

让我们还回到vuex官网下定义的那句话:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容