點燈坊

失くすものさえない今が強くなるチャンスよ

Vuex 使用 Functional API

Sam Xiao's Avatar 2020-11-12

傳統 Vuex 都以 this 使用,這使得 Extract Function 時還必須將 Vue Instance 傳進 Function,事實上 Vuex 也能不使用 this,直接 Import Store 使用,甚至更近一步 Point-free。

Version

Vue 2.6.11
Vuex 3.4.0

Options API

vuex000

使用 Vuex 實現 counter。

store/index.js

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

Vue.use(Vuex)

let addCount = state => state.count++

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    addCount
  }
})

第 9 行

state: {
  count: 0
},

定義 count state 初始值為 0

第 6 行

let addCount = state => state.count++

定義 addCount mutation,每次呼叫都對 count state 遞增。

App.vue

<template>
  <div>
    <button @click="onClick">+</button>
    {{ count }}
  </div>
</template>

<script>
let count = function() {
  return this.$store.state.count
}

let onClick = function() {
  this.$store.commit('addCount')
}

export default {
  name: 'App',
  computed: {
    count
  },
  methods: {
    onClick
  }
}
</script>

第 9 行

let count = function() {
  return this.$store.state.count
}

使用 count computed 處理 count state,當 count state 改變時,count computed 會隨之 reactive 改變。

傳統會使用 this.$store 讀取 Vuex,由與使用了 this,必須使用 function expression 而不能使用 arrow function,也不方便 extract function。

13 行

let onClick = function() {
  this.$store.commit('addCount')
}

使用 this.$storecommit() 呼叫 addCount mutation,由於使用了 this,必須使用 function expression 而不能使用 arrow function,也不方便 extract function。

Functional API

App.vue

<template>
  <div>
    <button @click="onClick">+</button>
    {{ count }}
  </div>
</template>

<script>
import store from '@/store'

let count = () => store.state.count

let onClick = () => store.commit('addCount')

export default {
  name: 'App',
  computed: {
    count
  },
  methods: {
    onClick
  }
}
</script>

第 9 行

import store from '@/store'

將實際 store 引入。

11 行

let count = () => store.state.count

直接以 store 使用 Vuex,由其下的 state 讀取 count state。

因為不再使用 this,因此可安心使用 arrow function。

13 行

let onClick = () => store.commit('addCount')

直接以 store 使用 Vuex,將 addCount mutation 傳入 commit() 執行之。

因為不再使用 this,因此可安心使用 arrow function。

Point-free

App.vue

<template>
  <div>
    <button @click="onClick">+</button>
    {{ count }}
  </div>
</template>

<script>
import { pipe, always, prop } from 'ramda'
import store from '@/store'

let { state, commit } = store

let count = pipe(
  always(state),
  prop('count')
)

let onClick = pipe(
  always('addCount'),
  commit
)

export default {
  name: 'App',
  computed: {
    count
  },
  methods: {
    onClick
  }
}
</script>

12 行

let { state, commit } = store

store 直接 destructure 出 statecommit()

14 行

let count = pipe(
  always(state),
  prop('count')
)
  • 使用 prop() 讀取 state.count,先透過 always() 準備 state

19 行

let onClick = pipe(
  always('addCount'),
  commit
)
  • 使用 commit() 執行 mutation,先透過 always() 準備 addCount

Conclusion

  • Vue 有多種寫法,其實不見得什麼都要使用 this ,Vuex 透過引用實際 store 可直接使用,不用再擔心很難處理 this
  • 既然不必使用 this,可直接 destructure 出 statecommit(),進一步使用 Function Pipeline 組合