vuex v2 中的一些变化

由于之前身体抱恙休假在家,于是对 vue/vuex 便没有多少关注,在上周回来投入到工作中时发现 vuex 2.0.0 相对于之前的版本有了部分变化,关于 vuex 最新版的设计在此 issue 中。

命名语义化


首先就是针对命名的语义化,主要集中在两个名词上:commitdispatch

之前的 store.dispatch 变成了现在的 store.commit,但是 store.dispatch 依然可用,但仅仅用来触发某些 action

首先,尤大认为用 dispatch 来使一个 mutation 发生改变在语义上是不正确的,dispatch 应当用来表明一个事件是我们有意触发的这样一个操作,但对于 mutation 来说,需要有一个动词来表示我们在做这个操作之后会触发状态改变,用他的原话来说就是:Dispatch should be indicating the intention for something to happen. For mutations, we want a verb that indicates the state change is happening as soon as you call it.

  • dispatch:表明有某些事件发生的意向(可能是异步操作产生的副作用)。
  • commit:说明会使实际状态发生改变的同步操作。

例子:

// before 2.0.0
actions: {
  'ACTION_TYPE': ({dispatch}, payload) => {
    dispatch(payload)
  }
}

// now
actions: {
  'ACTION_TYPE': ({ commit }, payload) => {
    commit('some-mutation', payload)
  }
// dispatch
store.dispatch('ACTION_TYPE')

从上面的代码中我们可以看出,在 vuex 2.0.0 中,commit 用来说明一个使 mutation 状态发生改变的操作,而 dispatch 则用来触发一个 action

模块的可移植性和可组合性


storemodule 中的 action

如果我们在模块内部定义 actions 时,如果多个模块定义相同名字的 action 时可能会产生部分问题,而且有的时候需要调用多个模块的 action,那么在这两种情况看来,如果我们把 action 定义为一个类似事件监听器的模式而不是直接调用它们,取而代之的是用 store.dispatch 去触发这个事件,这样的话我们就可以在一次调用中触发多个模块的 action,而且这种做法也能够明确的表明这个 action 所有可能产生的副作用。
例子:

const store = new Vuex.Store({
  actions: {
    doSomething: ({ commit }) => {
      commit('some-mutation')
    }
  }
})
// 你可以直接访问这个action
console.log(store.actions.doSomething)
// 触发这个action
store.dispatch('doSomething')

与之类似的还有 getters

我们同样可以直接在 store 中定义,也可以在 module 内部定义
例子:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  getters: {
    hasAny: state => state.count > 0
  }
})

// access the getter
store.getters.hasAny // -> false

事件流可组合

因为 dispatch 的存在,我们在需要一次操作触发多个 action 的情况下,那么事件流的可组合就会使得代码写起来游刃有余,尤其是结合 Promise 或者 async/await。比如我们有一个列表,在删除列表中的一项或多项之后,我们通常需要重新拉去一次列表或者将本地的列表数据重排,那么我们就可以在完成 deleteItem 之后通过 store.dispatch 来触发 getList 操作,与之形成对比的是,我们在 Redux 中所使用的 redux-saga
例子:

const store = new Vuex.Store({
  actions: {
    deleteItem: ({ commit }, payload) => {
      return callPromiseAPI(payload).then(res => {
         commit('delete', { res })
      })
    },
    getList: ({ commit }, payload) => {
      return callPromiseAPI(payload).then(res => {
         commit('list', { res })
      })
    }
  }
})

store.dispatch('deleteItem', { id: 1 }).then(() => {
  // action done
  store.dispatch(getList, {page: 1})
})
// 结合 async/await
const store = new Vuex.Store({
  actions: {
    one: async ({ commit }, payload) => {
      const res = await callPromiseAPI(payload)
      commit('some-mutation', { res })
    },
    two: async ({ dispatch, commit }) => {
      await dispatch('one')
      commit('done')
    }
  }
})

store.dispatch('two') // fires off complicated async flow

从上面的两部分代码你可能发现了两种写法,实际上两种写法的效果是一样的,只是 Promise.then()async/await 的区别

其他

出上面两个之外,Vuex@2 还提供了 mapGettersmapActions 两个辅助函数,在之前的版本中,我们在一个组件内部使用 gettersactions 通常都是通过下面种方式:


export default {
  vuex: {
    getters: {

    },
    actions: {

    }
  }
}

那么在有这两个辅助函数之后我们只需要这样:

import {mapActions, mapGetters} from 'vuex'

export default {
  computed: mapGetters(['a', 'b', 'c']),
  methods: mapActions(['doA', 'doB', 'doC'])
}

当然就像我们在第二部分中提到的,你可以通过下面中方式

export default {
  computed: {
    a: () {
      return this.$store.getters.a
    }
  },
  methods: {
    doB: () {
      this.$store.dispatch('b')
    }
  }
}

更多使用方法,请查看这个issue

另外 vuex@2 可能会随着 vue@2 一起发布,目前都处在 beta 阶段,欢迎入坑!

文章均采用 CC BY-NC-SA 3.0 许可协议,转载请注明出处。
本文链接:https://blog.kingsongao.com/2016/07/24/vuex-v2-中的一些变化/