半小时学会使用Vuex

·  阅读 2643
半小时学会使用Vuex

1.什么情况下需要使用Vuex?

  • 不适用:在小型项目中使用 Vuex 在写法上是比较繁琐冗余的,通常,一个简单的 store模式 就足够您所需了。
  • 适用于:构建一个中大型单页应用,Vuex可以更好地在组件外部管理状态,更方便于兄弟、父子、和跨级组件之间的状态共享,方法公用。

(1)多个视图使用同一状态/方法

  methods: {
    getData (data) {
      this.stage = data.stage
      this.$refs.detailInfo.getData(data)
      this.$refs.productInfo.getData(data)
      this.$refs.paymentPlan.getData(data)
      this.$refs.attachFile.getData()
      this.$refs.examRecord.getData()
      this.$refs.changeRecord.getData(data)
      this.$refs.contactInfo.getData(data)
      this.$refs.orderInfo.getData(data)
      this.$refs.ticketInfo.getData()
      this.$refs.paidInfo.getData()
    }
  }复制代码

上述代码中,由于很多个子组件都使用了父组件中的data, 或子组件的方法调用依赖于父组件的状态更新,这时候如果用普通的父子组件通信方式,写法上是非常的繁琐,并且对于兄弟组件间的状态传递也是很难受。

(2)不同视图需要改变同一状态

<template>
  <div>
    <Card class="mgB-15 tabs-style">
      <Tabs type="card">
        <TabPane label="详细信息">
          <detailInfo ref="detailInfo"/>
        </TabPane>
        <TabPane :label="label.productInfo">
          <productInfo ref="productInfo" @setLabel="setLabel" />
        </TabPane>
        <TabPane :label="label.paymentPlan">
          <paymentPlan ref="paymentPlan" @setLabel="setLabel" />
        </TabPane>
        <TabPane :label="label.attachFile">
          <attachFile ref="attachFile" @setLabel="setLabel" />
        </TabPane>
        <TabPane :label="label.examRecord">
          <examRecord ref="examRecord" @setLabel="setLabel" />
        </TabPane>
        <TabPane :label="label.changeRecord">
          <changeRecord ref="changeRecord" @setLabel="setLabel" />
        </TabPane>
        <TabPane :label="label.contactInfo">
          <contactInfo ref="contactInfo" @setLabel="setLabel"/>
        </TabPane>
        <TabPane :label="label.orderInfo" :disabled="disabled">
          <orderInfo ref="orderInfo" @setLabel="setLabel" />
        </TabPane>
        <TabPane :label="label.ticketInfo" :disabled="disabled">
          <ticketInfo ref="ticketInfo" @setLabel="setLabel" />
        </TabPane>
        <TabPane :label="label.paidInfo" :disabled="disabled">
          <paidInfo ref="paidInfo" @setLabel="setLabel" />
        </TabPane>
      </Tabs>
    </Card>
  </div>
</template>复制代码

上述代码:多个子组件要修改父组件的label值,采用子组件抛出事件来变更和同步父组件的状态,只是修改了一个状态都如此麻烦,当要修改的数据比较多的时候,通常会导致无法维护的代码。自己写完的代码都不想再去看了,这谁扛得住啊。。。

2.快速上手Vuex核心api

Vuex的五大核心:state、mutations、actions、getters、modules
  • state(辅助方法:mapState)

vuex使用的是单一的状态树,我们的vue应用将仅仅包含一个 store 的实例。所以当我们将store挂载到vue的实例上以后,我们可以通过this.$store取到vuex里面的各个部分。我们可以将state看作是单页面中的data,只不过state中的数据是公共的、全局的,任何组件都能实时访问到。如下:

export default new Vuex.Store({
  state: {
    username: 'admin',
    password: '123456',
    tenant: 1
  }
})复制代码

如果不使用mapState函数,在页面中访问state中的数据如下:瞧瞧,这得多麻烦呀

  export default {
    name: 'Home',
    mounted () {
      console.log(this.username, this.password, this.tenant)  // admin 123456 1 
    },
    computed: {
      username () {
        return this.$store.state.username
      },
      password () {
        return this.$store.state.password
      },
      tenant () {
        return this.$store.state.tenant
      },
  }复制代码

当使用mapState辅助函数时候,如下:看看,这就舒服多了啊

  import { mapState } from 'vuex'
  export default {
    name: 'Home',
    mounted () {
      console.log(this.username, this.password, this.tenant)  // admin 123456 1 
    },
    computed: {
      ...mapState([
        'username',
        'password',
        'tenant'
      ])
    }
  }复制代码

小结:mapState的实际作用就是当一个组件需要获取多个状态时候,如果将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。

  • mutations(辅助方法:mapMutations)
更改state中数据的唯一方法是通过mutations。一条重要的原则就是要记住 mutations中的方法 必须是同步函数,并且是以commit方式触发。参数为:(state, '传递的参数')如下:

export default new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    addCount (state, num) {
      state.count = num
    }
  }
})复制代码

如下:你可以在组件中使用 this.$store.commit('方法名', '传递的参数') 提交 mutations

  import { mapState } from 'vuex'
  export default {
    name: 'Home',
    mounted () {
      this.$store.commit('addCount', 666)
      console.log(this.count)   //666
    },
    computed: {
      ...mapState([
        'count'
      ])
    }
  }复制代码

mapMutations的写法和上文提到的mapState的写法基本一致, 这里我们主要使用数组的形式来说明。一切,都是语法糖...

  import { mapState, mapMutations } from 'vuex'
  export default {
    name: 'Home',
    mounted () {
      this.addCount(777)
      console.log(this.count)  //777
    },
    methods: {
      ...mapMutations([
        'addCount'
      ])
    },
    computed: {
      ...mapState([
        'count'
      ])
    }
  }复制代码
  • actions(辅助方法:mapActions)

actions 类似于 mutations,不同在于:

(1)actions 的方法是通过dispatch方式触发,在方法内不能直接变更state, 只能通过commit提交mutations中的方法去修改。

(2)actions中 可以包含任意异步操作,通常用来写一些公共的异步请求方法

import { query } from '@/api/index'   //这是获取用户信息的api方法

export default new Vuex.Store({
  state: {
    userInfo: {}
  },
  mutations: {
    setUser (state, data) {
      state.userInfo = data
    }
  },
  actions: {
    getUser (context) {
      console.log(context, 'context信息')
      return query().then(res => {
        context.commit('setUser', res.data)
        return res
      })
    }
  }
})复制代码

如下:你可以在组件中使用 this.$store.dispatch('方法名') 提交 actions

  import { mapState, mapMutations } from 'vuex'
  export default {
    name: 'Home',
    mounted () {
      this.$store.dispatch('getUser').then(res => {
        console.log(this.userInfo, '获取用户登录信息')
      })
    },
    methods: {
      ...mapMutations([
        'setUser'
      ])
    },
    computed: {
      ...mapState([
        'userInfo'
      ])
    }
  }复制代码

上述代码打印如下:


mapActions的写法和上文提到的mapState,mapMutations的写法也基本一致, 这里我们主要使用数组的形式来说明。一切,也都是语法糖...

  import { mapState, mapMutations, mapActions } from 'vuex'
  export default {
    name: 'Home',
    mounted () {
      this.getUser().then(res => {     
        console.log(this.userInfo, '999')
      })
    },
    methods: {
      ...mapMutations([
        'setUser'    
      ]),
      ...mapActions([  
        'getUser'
      ])
    },
    computed: {
      ...mapState([
        'userInfo'
      ])
    }
  }复制代码

  • getters(辅助方法:mapGetters)
getters类似于单页面中的computed方法,可以认为是store中的计算属性,只有当它的依赖值发生了改变,才会重新计算。

辅助函数其实就是个语法糖,方便我们去使用store,鉴于上面啰嗦太多,现在可以直接使用mapGetters辅助方法了吧,翠花,上代码:

export default new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    addCount (state, data) {
      state.count = data
    }
  },
  getters: {
    newCount (state) {
      return state.count * 2
    }
  }
})复制代码

上述代码:newCount的值是根据count的值计算而来的。在页面获取newCount值如下:

  import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
  export default {
    name: 'Home',
    mounted () {
      this.addCount(18)
      console.log(this.newCount, '根据count计算而来')    // 36 "根据count计算而来"
    },
    methods: {
      ...mapMutations([
        'addCount'
      ])
    },
    computed: {
      ...mapState([
        'userInfo'
      ]),
      ...mapGetters([
        'newCount'
      ])
    }
  }复制代码

  • modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块

如下:

// 这是index.js文件
import user from '@/store/modules/user'
import contract from '@/store/modules/contract'
export default new Vuex.Store({
  modules: {
    user,
    contract
  }
})复制代码

既然分了模块,那么如果各个模块的方法名难免会有重名的情况,该怎么办呢?这里可以设置namespaced: true  使用命名空间来解决这个问题。具体如下:

// 这是user.js文件
export default {
  namespaced: true,
  state: {
    count: 10
  },
  mutations: {
    setCount (state, num) {
      state.count = num
    }
  },
  actions: {
    getCount ({commit}) {
      commit('setCount', 6)
    }
  },
  getters: {
    newCount (state) {
      return state.count * 6
    }
  }
}
复制代码

//这是contract.js文件
export default {
  namespaced: true,
  state: {
    count: 20
  },
  mutations: {
    setCount (state, num) {
      state.count = num
    }
  },
  actions: {
    getCount ({commit}) {
      commit('setCount', 6)
    }
  },
  getters: {
    newCount (state) {
      return state.count * 2
    }
  }
}复制代码

我们可以看到,两个store模块的文件方法名全部一样,开启命名空间后,在页面上使用如下:

  import { mapState, mapMutations, mapGetters } from 'vuex'
  export default {
    name: 'Home',
    mounted () {
      console.log(this.user, this.contract)    // { count: 10} , { count: 20}
      console.log(this.newCount,this.newCount1);    // 40 60
    },
    methods: {
      ...mapMutations([
        'addCount'
      ])
    },
    computed: {
      ...mapState([
        'user',
        'contract'
      ]),
      ...mapGetters({
        newCount: 'user/newCount',    // 使用命名空间,可以在页面重新定义名字,使得各模块的方法名即使一样也不会冲突
        newCount1: 'contract/newCount',
      })
    }
  }复制代码


分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改