当前位置:   article > 正文

vuex详解一:彻底弄懂state、mapState、mapGetters、mapMutations、mapActions_vue mapstate

vue mapstate

一 、state

先看一下标准的store目录结构

入vuex 以后,我们需要在state中定义变量,类似于vue中的data,通过state来存放共享的1状态

  1. -store
  2. --actions
  3. --mutations
  4. --getters
  5. --mutations-type
  6. --index.js复制代码

index.js是总入口

  1. importVuefrom'vue'importVuexfrom'vuex'Vue.use(Vuex)
  2. import state from'./state.js'import actions from'./actions.js'import mutations from'./mutations.js'import getters from'./getters.js'exportdefaultnewVuex.Store({
  3. state,
  4. actions,
  5. mutations,
  6. getters
  7. })
  8. 复制代码

我们在state.js中定义变量

  1. exportdefault {
  2. userInfo:{
  3. userName:"油炸皮卡丘",
  4. age:"22",
  5. sex:"女"
  6. },
  7. token:"aerfrftregtyrb.rytyuhjyi",
  8. friends:["Tom","Herry","Lucy"],
  9. likes:["吃饭",'睡觉','看电影'],
  10. money:250
  11. }
  12. 复制代码

在组件中使用,在App.vue中自定义两个组件

<divid="app"><about></about><home></home></div>复制代码

about.vue里面的内容

  1. <h1>{{$store.state.userInfo.userName}}</h1> //油炸皮卡丘
  2. 复制代码

home.vue里面的内容

  1. <h1>{{$store.state.userInfo.age}}</h1> //22
  2. 复制代码

如图,显示出显示出相应的内容,有了vuex,我们不必在考虑组件之间的传值,直接就可以通过$store来获取不同的数据

但是如果需要vuex中的多个数据的这时候,这样写就太啰嗦了,我们可以将它定义在computed中。

props,methods,data和computed的初始化都是在beforeCreated和created之间完成的。

例如

  1. <template>
  2. <divclass="home">
  3. {{userName}}
  4. </div>
  5. </template>
  6. <script>exportdefault {
  7. name: 'home',
  8. computed:{
  9. userName(){
  10. returnthis.$store.state.userInfo.userName
  11. }
  12. }
  13. }
  14. </script>复制代码

这样引入就方便了很多。

1.2 mapState辅助函数

使用this.$store.state虽然可以很方便的将state里面的值融入computed,但是如果要取多个值,就会出现以下的情况

  1. computed:{
  2. userInfo(){
  3. returnthis.$store.state.userInfo
  4. },
  5. token(){
  6. returnthis.$store.state.token
  7. },
  8. friends(){
  9. returnthis.$store.state.friends
  10. },
  11. likes(){
  12. returnthis.$store.state.likes
  13. },
  14. ...
  15. }
  16. 复制代码

特别多的话就会很麻烦,而这时候vuex又给我们提供了更简便的方法mapState方法,

该方法就可以自动将需要的state值映射为实例的计算属性

我们可以这样写

  1. import {mapState} from'vuex'exportdefault {
  2. name: 'home',
  3. computed: mapState(['likes','friends','token','userInfo'])
  4. }
  5. 复制代码
mapState(['likes','friends','token','userInfo']) //填入哪些字段就会将这些字段自动映射为一个计算属性复制代码

所以

  1. mapState(['likes','friends','token','userInfo'])
  2. 复制代码

等价于下面的代码

  1. userInfo(){
  2. returnthis.$store.state.userInfo
  3. },
  4. token(){
  5. returnthis.$store.state.token
  6. },
  7. friends(){
  8. returnthis.$store.state.friends
  9. },
  10. likes(){
  11. returnthis.$store.state.likes
  12. },
  13. 复制代码

记住:用mapState等这种辅助函数的时候,前面的方法名和获取的属性名是一致的。

如果我们需要自定义一个计算属性怎么办呢?怎么添加呢?

毕竟现在已经成这样了 computed: mapState(['likes','friends','token','userInfo'])

这时候我们就需要es6中的展开运算符:...。将mapState映射的计算属性追加上去,而不是覆盖原有的

  1. computed: {
  2. value(){
  3. returnthis.val++
  4. },
  5. ...mapState(['likes','friends','token','userInfo'])
  6. }
  7. 复制代码

在模板中使用

  1. <h2>{{userInfo.userName}}</h2> //油炸皮卡丘
  2. 复制代码

关于别名

有时候,我们映射的时候,想给计算属性起个新的名字,不想用原有的属性名,那就可以像下面这样做,用对象的形式去别名

  1. computed: {
  2. value(){
  3. returnthis.val++
  4. },
  5. ...mapState({
  6. myLikes:"likes",
  7. myFriends:"friends",
  8. theUserInfo:"userInfo"
  9. })
  10. }
  11. 复制代码

这样我们就可以在模板中使用

  1. <h2>{{theUserInfo.userName}}</h2> //油炸皮卡丘
  2. 复制代码

二、getters

getters相当于vue中的计算属性,能拿到state里面最新的值

而且getters允许传参,第一个参数就是state

这样,通过getters进一步处理,得到我们想要的值,

getters.js

  1. exportdefault{
  2. realName:(state)=>{
  3. return state.userInfo.userName+''
  4. },
  5. myMoney:(state)=>{
  6. return (state.money/7).toFixed(2)
  7. }
  8. }
  9. 复制代码

在实例中我们可以这样用

  1. computed: {
  2. valued(){
  3. returnthis.value++
  4. },
  5. realName:this.$store.getters.realName,
  6. myMoney:this.$store.getters.myMoney,
  7. }
  8. 复制代码

2.2 mapGetters辅助函数

mapGetters函数具有mapState的作用,而且其主要用法也是一样的,也能将getters的属性映射到实例的计算属性

  1. computed: {
  2. valued(){
  3. returnthis.value++
  4. },
  5. ...mapGetters(['realName','myMoney'])
  6. }
  7. 复制代码

同样也可以取别名

  1. computed: {
  2. valued(){
  3. returnthis.value++
  4. },
  5. ...mapGetters({
  6. myRealName:"realName",
  7. myFormatMoney:"myMoney"
  8. })
  9. }
  10. 复制代码

但有人就要问了,二者的作用实际上都一样?到底用哪个?或者说二者还有什么实质上的区别?

上面说过,getters可以传参,这才是getters和mapState的区别

那么如何传参呢?

getters的第一个参数是state

有这样一个场景,我们不想取所有的state里面的friends值,而是想根据我们传入的值筛选一下

那我们只要在getter返回的值套上一层函数那我们就可以实现传参数

  1. exportdefault{
  2. realName:(state)=>{
  3. return state.userInfo.userName+''
  4. },
  5. myMoney:(state)=>{
  6. return (state.money/7).toFixed(2)
  7. },
  8. myFriend:(state)=>{
  9. //返回一个函数用于接收我们传入的筛选值returnfunction (friendName) {
  10. return state.friends.find(item => item === friendName)
  11. }
  12. }
  13. }
  14. 复制代码

很显然,getters不仅可以像mapState一样拿到state里面的值,而且还可以在拿到之前,对值进行我们想要的加工

从而“派生”一些新的值,以满足我们不同的业务需要

淡然,如果不需要派生新的值,

  1. this.$store.getters.值 就等于 this.$store.state.值
  2. 复制代码

三、mutation

我们代码中定义的时候需要些mutations,它类似于vue中的methods,

mutations需要通过commit来调用其里面的方法,它也可以传入参数,第一个参数是state,第二个参数是载荷(payLoad),也就是额外的参数

我们只能通过mutation去更改state里面的值

mutations.js

  1. exportdefault {
  2. addAge:(state,payLoad)=>{
  3. state.userInfo.age+=payLoad.number
  4. },
  5. changeName:(state,payLoad)=>{
  6. state.userInfo.userName+=payLoad.userName
  7. },
  8. addFriend:(state,payLoad)=>{
  9. if(state.friends.indexOf(payLoad.newFriend) < 0){
  10. state.friends.push(payLoad.newFriend)
  11. }
  12. },
  13. setUserInfo:(state,payLoad)=>{
  14. state.userInfo=payLoad.userInfo
  15. },
  16. setToken:(state,payLoad)=>{
  17. state.token=payLoad.token
  18. }
  19. }
  20. 复制代码

在模板中使用

  1. <div class="home">
  2. <button @click="handleAddAge">增加一岁</button>
  3. <button @click="handleAddFriend">增加一个朋友</button>
  4. </div>
  5. 复制代码

js部分

  1. methods:{
  2. handleAddAge(){
  3. //使用mutation不像之前state、getters一样直接点调用,而是用commit关键字来提交mutationthis.$store.commit('addAge',{
  4. number:1
  5. })
  6. },
  7. handleAddFriend(){
  8. let name="皮卡丘";
  9. this.$store.commit('addFriend',{
  10. newFriend:name
  11. })
  12. }
  13. }
  14. 复制代码

调用的时候第二个参数最好写成对象形式,这样我们就可以传递更多信息。

  1. this.$store.commit('mutationName',{
  2. key1:val1,
  3. key2:val2,
  4. key3:val3
  5. })
  6. 复制代码

但是,这样写还是会遇到同样的问题,就是如果需要操作多个数据,就会变的麻烦,这时候我们就需要mapMutations,通过它将方法映射过来

3.1 mapMutations辅助函数

跟mapState、mapGetters一样

不同的是,mapMutations是将所有mutations里面的方法映射为实例methods里面的方法

所以我们可以这样用

  1. methods:{
  2. ...mapMutations(['addAge'])
  3. }
  4. 复制代码

mapMutations(['addAge'])这一句就相当于下面的代码

  1. methods:{
  2. addAge(payLoad){
  3. this.$store.commit('addAge',payLoad)
  4. }
  5. }
  6. 复制代码

参数我们可以在调用这个方法的时候写入

  1. <button @click="AddAge({number:1})">增加一岁</button>
  2. 复制代码

同样也可以有别名

  1. methods:{
  2. ...mapMutations({
  3. handleAddAge:'addAge'
  4. })
  5. }
  6. 复制代码
  1. <button @click="handleAddAge({number:1})">增加一岁</button>
  2. 复制代码

这时候有些人要说了,我为什么要绕一圈,从mutations里面去改state呢?我能不能直接改state呢?

比如这样:

  1. addAge(){
  2. this.$store.state.userInfo.age +=5;
  3. }
  4. 复制代码

实际看结果也可以,那我为什么从mutations里面中转一下呢?

原因如下:

  • 在mutations中不仅仅能做赋值操作

  • Vue.js在mutations中做了类似埋点操作,如果从mutations中操作的话, 能被检测到,可以更方便用调试工具调试,调试工具可以检测到实时变化,而直接改变state中的属性,则无法实时监测

注意:mutations只能写同步方法,不能写异步,比如axios、setTimeout等,这些都不能写,mutations的主要作用就是为了修改state的。
原因类似:如果在mutations中写异步,也能够调成功,但是由于是异步的,不能被调试工具追踪到,所有不推荐这样写,不利于调试,这是官方的约定。

3.2 使用常量替代Mutation事件类型

把原本的方法名称由字符串转变成常量

mutations.js

  1. constADD_AGE ='addAge'constCHANGE_NAME ='changeName'constADD_FRIEND='addFriend'constSET_USER_INFO='setUserInfo'constSET_TOKEN='setToken'//然后用常量替换原有的方法名exportdefault {
  2. [ADD_AGE](state,payLoad){
  3. state.userInfo.age+=payLoad.number
  4. },
  5. [CHANGE_NAME](state,payLoad){
  6. state.userInfo.userName+=payLoad.userName
  7. },
  8. [ADD_FRIEND](state,payLoad){
  9. if(state.friends.indexOf(payLoad.newFriend) < 0){
  10. state.friends.push(payLoad.newFriend)
  11. }
  12. },
  13. [SET_USER_INFO](state,payLoad){
  14. state.userInfo=payLoad.userInfo
  15. },
  16. [SET_TOKEN]:(state,payLoad)=>{
  17. state.token=payLoad.token
  18. }
  19. }
  20. 复制代码

为什么这样写?

  • 不容易写错,字符串容易写错,而且字符串写错以后不会报错位置,而用常量替代,如果写错,eslint可以提示错误位置

  • 当使用action派发mutation时,在action中使用同样的常量,避免手滑写错方法

用常量替代mutations的时候我我们可以新建一个文件(mutation_type.js)专门存储这些常量

mutation_type.js部分

  1. constADD_AGE ='addAge'constCHANGE_NAME ='changeName'constADD_FRIEND='addFriend'constSET_USER_INFO='setUserInfo'constSET_TOKEN='setToken'export {
  2. ADD_AGE,
  3. CHANGE_NAME ,
  4. ADD_FRIEND,
  5. SET_USER_INFO,
  6. SET_TOKEN
  7. }
  8. 复制代码

然后在mutations.js中引入

  1. import {
  2. ADD_AGE,
  3. CHANGE_NAME,
  4. ADD_FRIEND,
  5. SET_USER_INFO,
  6. SET_TOKEN
  7. } from"./mutation_type.js"exportdefault {
  8. [ADD_AGE](state,payLoad){
  9. state.userInfo.age+=payLoad.number
  10. },
  11. [CHANGE_NAME](state,payLoad){
  12. state.userInfo.userName+=payLoad.userName
  13. },
  14. [ADD_FRIEND](state,payLoad){
  15. if(state.friends.indexOf(payLoad.newFriend) < 0){
  16. state.friends.push(payLoad.newFriend)
  17. }
  18. },
  19. [SET_USER_INFO](state,payLoad){
  20. state.userInfo=payLoad.userInfo
  21. },
  22. [SET_TOKEN]:(state,payLoad)=>{
  23. state.token=payLoad.token
  24. }
  25. }
  26. 复制代码

四、actions

action类似于mutation

我们只需要记住一下几点:

  • action可以提交mutation,然后mutation去更改state

  • action不要直接去操作state,而是去操作mutation

  • action包含异步操作,类似于axios请求,可以都放在action中写

  • action中的方法默认的就是异步,并且返回promise

为什么?因为这是Vuex规定的

actions.js

  1. import {
  2. ADD_AGE,
  3. CHANGE_NAME,
  4. ADD_FRIEND,
  5. SET_USER_INFO,
  6. SET_TOKEN
  7. } from"./mutation_type.js"exportdefault{
  8. //定义一个异步获取用户信息的actionasyncgetUserInfo(context){
  9. //context可以理解为它是整个Store的对象.类似于this.$store,里面包含了state,getter,mutations,actionsconst res = await axios.get('/接口url')
  10. //这个时候就用到了 mutation_type.js
  11. context.commit( SET_USER_INFO,{userInfo:res.userData}
  12. )
  13. },
  14. //定义一个异步获取用户token的actionasyncgetToken(context){
  15. const res = await axios.get('/接口url')
  16. context.commit(
  17. SET_TOKEN,
  18. {token:res.token}
  19. )
  20. }
  21. }
  22. 复制代码

当然,我们可以通过解构的方式,将context对象里面的属性解构

  1. asyncgetUserInfo:({commit,dispatch})=>{
  2. const res = await axios.get('/接口url')
  3. commit( SET_USER_INFO, {userInfo:res.userData})
  4. }
  5. 复制代码

想想一个实际开发场景,state里面的userInfo属性值是空的,当登录以后,再进行获取对应的信息。

登录以后,需要得到用户信息显示到界面上,那如何得到呢?

首先进入页面的时候调用actions中的getUserInfo方法

vue部分

  1. <template>
  2. <div>{{realName}}</div>
  3. </template>
  4. exportdefault{
  5. computed(){
  6. //第三步realName:this.$store.getters.realName
  7. },
  8. created(){
  9. //第一步this.reqUserInfo()
  10. },
  11. methods:{
  12. reqUserInfo(){
  13. //第二步使用action不想之前state、getters一样直接点调用,而是用dispatch关键字来派发actionthis.$store.dispatch('getUserInfo')
  14. }
  15. }
  16. }
  17. 复制代码

来梳理一下上面的流程

  1. 页面初始化时调用 this.reqUserInfo()方法。 this.reqUserInfo()派发一个名为getUserInfo的action

  1. 在getUserInfo这个action中,我们执行了如下操作:

  1. asyncgetUserInfo(context){
  2. //1.从接口里异步获取数据const res = await axios.get('/接口url')
  3. //2.将获取到的数据,通过commit提交一个mutation,去更改state里面的userInfo
  4. context.commit( SET_USER_INFO,{userInfo:res.userData})
  5. },
  6. 复制代码
  1. 在computed中拿到最新的用户信息的用户名

  1. //3.拿到最新的userInfo
  2. realName:this.$store.getters.realName
  3. 复制代码

所以看到这里,大家应该明白Vuex所谓的单向数据流

界面——>派发action——>action提交mutation——>mutation更改state——>getters返回最新的state值到界面

4.1 mapActions辅助函数

mapActions和mapMutations一样,它将actions里面的方法映射到methods

所以,当action多了,我们可以这样使用

  1. methods:{
  2. ...mapActions(['getUserInfo','getToken'])
  3. }
  4. 复制代码

相当于

  1. methods:{
  2. getUserInfo(){
  3. returnthis.$store.dispatch(‘getUserInfo’)
  4. },
  5. getToken(){
  6. returnthis.$store.dispatch(‘getToken’)
  7. },
  8. }
  9. 复制代码

当然也可以有别名

  1. methods:{
  2. ...mapActions(
  3. {reqUserData:'getUserInfo'},
  4. {reqToken:'getToken'},
  5. )
  6. }
  7. 复制代码

另外,补充一下

可以在action派发别的acton,如下

  1. asyncgetUserInfo(context){
  2. const res = await axios.get('/接口url')
  3. context.commit( SET_USER_INFO,{userInfo:res.userData})
  4. //此处派发另外一个action
  5. context.dispatch('getToken')
  6. },
  7. 复制代码

5、总结一下

  • 依赖state得到新的数据,用this.$store.getters.值

  • 直接获取state的数据,用this.$store.state.值

  • 同步修改state的属性值,就用this.$store.commit('mutation值')

  • 异步修改state的属性值,就用this.$store.dispatch('action值')

  • mapState、mapMutations、mapGetters、mapActions等辅助函数,便于我们处理多个方法和属性

作者:清风夜半

链接:https://juejin.cn/post/6969553033545941022

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/256894
推荐阅读
相关标签
  

闽ICP备14008679号