【Vue3】コンポジション関数でのv-modelとVuexの同期

Vue3でVue **Composition API**が導入され、特に**コンポジション関数**が使えるようになってからは、風景が一変したように感じられます。極端な言い方が許されれば、オブジェクト指向プログラミングから関数型プログラミングへの移行、とちょっとだけ考えてしまいます。個人的には、関数型言語が好きでオブジェクト指向は苦手です。ビジネス分野ではJavaが圧倒的ですが。

さて今回は**v-modelとVuexの同期**について、コンポジション関数がらみで少し疑問点があったので、試してみました。その備忘録です。

**【参考サイト】**
[Vue の Options API を Composition API に書き換える](https://zenn.dev/torish/scraps/dc5b03c85dae55) 2022/7/4追加
[コンポーネントを小さく・きれいに設計しようVue Composition APIを活用したコンポーネント分割術](https://ics.media/entry/210929/)
[How To Use V-Model With Vuex](https://dilshankelsen.com/v-model-with-vuex/)



# １．共通

メインです

```ts:main.ts
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

createApp(App).use(store).mount('#app')
```

Vuexのストアです

```ts:store/index.ts
import { createStore } from 'vuex'

export default createStore({
  state: {
    message: "hello"
  },
  mutations: {
    updateMessage(state, value) {
      state.message = value
    }
  },
  actions: {
  },
  modules: {
  }
})
```


#２． コンポジション関数を使わない場合


App.vueです。ひとつのファイルで定義しています。ここでのポイントは、**v-modelとVuexにおいてmessageが同期されている**、ということです。Vuexのget()とset()で実現しています。


```vue:App.vue
<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
      <input v-model="message">
      <p> {{ message }} </p>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue';
import { useStore } from 'vuex'

export default defineComponent({
  setup() {
    const store = useStore()
    const message = computed ({
      get() {
        return store.state.message
      },
      set(value) {
        store.commit('updateMessage', value)
      }
    })
    return { message }
  }
});
</script>

<style>
---
</style>
```


動作画面です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/714af5cd-2731-7fe1-a09c-2a7aab29c964.png)




#３． コンポジション関数を使った場合


さてここから本番です。App.vueからcomputed のロジックをuseHelloWorld.tsというコンポジション関数へと切り出します。

```vue:App.vue
<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
      <input v-model="message">
      <p> {{ message }} </p>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { useHelloWorld } from './useHelloWorld';

export default defineComponent({
  setup(props) {
    const message = useHelloWorld()
    return { message }
  }
});
</script>

<style>
---
</style>
```

Vuexのget()とset()はuseHelloWorld.tsでカプセル化され、App.vueからは隠蔽されます。App.vueはクリーンなコードになりました。動作は修正前と同じです。


```ts:useHelloWorld.ts
import { computed } from 'vue'
import { useStore } from 'vuex'

export const useHelloWorld = () => {
  const store = useStore()
  const message = computed ({
    get() {
      return store.state.message
    },
    set(value) {
      store.commit('updateMessage', value)
    }
  })
  return message
}
```


しかしどうでしょう？ App.vueのv-modelのmessageとVuexのmessageが同期しているメカニズムが、見えにくくなっています。可読性に問題がでるかもしれませんが、この程度の抽象化はよしかもしれません。

 コンポジション関数はとても有効だと思われますので、また今後何か書くかもしれません。


#【補足】2021/12/29 script setup

App.vueをscript setup文法で書き換えてみました。Vue.js 3.2ではこちらが推奨だそうなので。確かにすごくシンプルに書けていいですね。


```vue:App.vue
<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
      <input v-model="message">
      <p> {{ message }} </p>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { useHelloWorld } from './useHelloWorld';

const message = useHelloWorld()
</script>

<style>
---
</style>
```