vuex多种使用方式和map辅助函数

日期:2020年07月27日 阅读次数:4133 分类:Vue

一、前言

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

每一个 Vuex 应用的核心就是 store(仓库)。看起来很像一个全局对象,但又不是全局对象。
Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

二、哪些数据应该放在vuex中

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

三、vuex的工作流程

  1. 在视图中通过事件触发对应方法。在方法中的操作分以下两种情况。
    a、 如果是同步操作,则直接使用this.$store.commit提交一个mutations,让mutations去修改state的值。
    b、 如果有异步操作,则先使用this.$store.dispatch派发一个actions。在actions中处理异步操作(如:请求接口获取数据)然后在actions中再提交一个mutations,再让mutations去修改state的值。

  2. 在mutations中去修改state的值。

  3. state的值变化,再影响到视图的渲染。

下图说明了vuex的工作流程

vuex流程图

四、vuex使用方式

接下来我们以“计数器”实例来讲述

4.1、传统形式(this.$store)

store文件

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  getters: {
    // getter可以认为是store的计算属性
    // getter正常接受两个参数,分别表示 state, getters(在使用module时,还会有根store的state和getters)
    doubleCount: (state, getters) => {
      return state.count * 2
    }
  },
  mutations: {
    increment (state) {
      state.count++
    },
    addForNum (state, payload) {
      state.count += Number(payload.num)
    },
    asyncAdd (state) {
      state.count += 10
    }
  },
  actions: {
    // 异步操作需要放在actions中执行,然后使用commit交给对应的mutations修改state的值
    asyncAddAction (context) {
      setTimeout (function () {
        context.commit('asyncAdd')
      }, 500)
    }
  }
})

export default store

组件文件

模板部分

<template>
  <div>
    <h2>这是计数器页面</h2>
    <div>
      <div>计数:{{count}}</div>
      <div>双倍计数:{{doubleCount}}</div>
      <br/>
      <button @click="increment">+1</button>
      <div>
        增加值 <input type="text" v-model="num" />
        <button @click="addForNum">=</button>
      </div>
      <div>
        <button @click="asyncAdd">异步加10</button>
      </div>
    </div>
  </div>
</template>

javascript部分

export default {
  name: 'index',
  data () {
    return {
      num: 0
    }
  },
  computed: {
    count () {
      return this.store.state.count
    },
    doubleCount () {
      return this.store.getters.doubleCount
    }
  },
  methods: {
    // 自增1
    increment () {
      this.store.commit('increment')
    },
    // 加指定的值
    addForNum () {
      this.store.commit('addForNum', {num: this.num})
    },
    // 异步加1
    asyncAdd () {
      this.$store.dispatch('asyncAddAction')
    }
  }
}

4.2、使用map辅助函数

我们可以使用map辅助函数使代码更精简

store文件中的内容不需要变动,只要在组件中使用map辅助函数来替代全局的this.$sotre下的属性和方法即可。

组件文件修改如下:

模板部分

<template>
  <div>
    <h2>这是计数器页面</h2>
    <div>
      <div>计数:{{count}}  -  {{countAlias}}</div>
      <div>双倍计数:{{doubleCount}}  -  {{doubleCountAlias}}</div>
      <br/>
      <button @click="increment">+1</button>
      <div>
        增加值 <input type="text" v-model="num" />
        <button @click="incrementBy(num)">=</button>
      </div>
      <div>
        <button @click="asyncAdd">异步加10</button>
      </div>
    </div>
  </div>
</template>

javascript部分

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

export default {
  name: 'index',
  data () {
    return {
      num: 0,
      localCount: 10
    }
  },
  computed: {

    /*
    * ------------------------------------------------------------
    * mapState
    */

    // 【传统形式】
    // count () {
    //   return this.store.state.count
    // },

    // 【对象形式】
    ...mapState({
      // 箭头函数可使代码更简练
      count: state => state.count,

      // 传字符串参数 'count' 等同于 `state => state.count`
      // 使用这种方式可以更简洁地为state生成的计算属性定义一个别名
      countAlias: 'count',

      // 为了能够使用 `this` 获取局部状态,必须使用常规函数
      countPlusLocalState (state) {
        return state.count + this.localCount
      }
    }),

    // 【数组形式】
    // 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
    ...mapState([
      // 映射 this.count 为 store.state.count
      'count'
    ]),



    /*
    * ------------------------------------------------------------
    * mapGetters
    */

    // 【传统形式】
    // doubleCount () {
    //   return this.store.getters.doubleCount
    // }

    // 【对象形式】
    // 如果你想将一个 getter 属性另取一个名字,使用对象形式
    ...mapGetters({
      // 把 `this.doubleCountAlias` 映射为 `this.store.getters.doubleCount`
      doubleCountAlias: 'doubleCount'
    }),

    // 【数组形式】
    ...mapGetters(['doubleCount'])
  },
  methods: {

    /*
    * ------------------------------------------------------------
    * mapMutations
    */

    // 【传统形式】
    // 自增1
    // increment () {
    //   this.store.commit('increment')
    // },
    // // 加指定的值
    // incrementBy (amount) {
    //   this.store.commit('incrementBy', amount)
    // },

    // 【数组形式】
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.store.commit('incrementBy', amount)`
    ]),

    // 【对象形式】
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.store.commit('increment')`
    }),

    // // 异步加1
    // asyncAdd () {
    //   this.store.dispatch('asyncAddAction')
    // }

    /*
    * ------------------------------------------------------------
    * mapActions
    */

   // 【数组形式】
    ...mapActions([
      'asyncAddAction', // 将 `this.asyncAddAction()` 映射为 `this.store.dispatch('asyncAddAction')`
    ]),

    // 【对象形式】
    ...mapActions({
      asyncAdd: 'asyncAddAction' // 将 `this.asyncAdd()` 映射为 `this.$store.dispatch('asyncAddAction')`
    })
  }
}

五、总结

上面讲到了利用【this.$store】和【map辅助函数】两种方式来使用vuex,同样也可以两者混合使用。不过建议还是使用其中一种方式,使代码风格统一比较好。个人建议使用【map辅助函数】风格更好一些,因为【map辅助函数】显的代码更精简。当前还是要以自己的习惯为主,你可以自己选择自己喜欢的风格。

文章标签: