1
Help us understand the problem. What are the problem?

posted at

Vue.js+Vuexの行儀の悪い脱法コード3つ

これは「「はじめに」の Advent Calendar 2021」13日目の記事です。

※コンポーネントの種類、用語は Atomic design をベースにしています。

脱法1. Atom/MoleculesからActionを呼び出す

  • 理由
    • Atom/MoleculesとVuexのstateが強い結合をもつ。
    • 挙動が一緒で読み出すstateが違うパーツが大量生産される
FooPage.vue
<template>
  <ButtonFoo />
  <ButtonBar />
  <ButtonBaz />
</template>
<script>
import ButtonFoo from '@/components/ButtonFoo.vue'
import ButtonBar from '@/components/ButtonBar.vue'
import ButtonBaz from '@/components/ButtonBaz.vue'

  components: {
    ButtonFoo, ButtonBar, ButtonBaz
  }
</script>
ButtonFoo.vue
<template>
  <button @click="onClick">{{ name }}</button>
</template>
<script>

export default {
  computed: {
    name() {
      return this.$store.getters.foo.name;
    }
  },
  methods: {
    ..mapActions('foo', ['getName'])
    onClick() {
      this.getName()
    }
  }
}
</script>
store/modules/foo.js
export default {
  namespaced: true,
  mutations: {
    FOO_NAME: (state, name) => {
      state.name = name
    }
  },
  actions: {
    getName({ commit }) {
      asyncApi().then(response => {
        commit('FOO_NAME', response.data)
      }
    }
  }
}
  • 合法化するには
    • Atom/Moleculesへ値を渡すときはprops
    • Atom/Moleculesでの操作やイベントは$emit
    • Actionの呼び出しはPagesに集約させる
FooPage.vue
<template>
  <ButtonFoo :name="foo" @foo-click="getFoo()" />
  <ButtonFoo :name="bar" @foo-click="getBar()" />
  <ButtonFoo :name="baz" @foo-click="getBaz()" />
</template>
<script>
import ButtonFoo from '@/components/ButtonFoo.vue'

export default {
  computed: {
    foo(): { return this.$store.getters.foo.name; },
    bar(): { return this.$store.getters.bar.name; },
    baz(): { return this.$store.getters.baz.name; },
  },
  methods: {
    ..mapActions('foo', ['getName'])
    getFoo() {
      foo.getName()
    }
    // (略)
  }
}
</script>
Button.vue
<template>
  <button @click="onClick">{{ name }}</button>
</template>
<script>

export default {
  props: {
    name: {
      type: String,
      default: "init"
    }
  },
  methods: {
    onClick() {
      this.$emit()
    }
  }
}
</script>

脱法2: actionの戻り値をHookして自コンポーネント内のdataに保存する

  • 理由
    • Vuexの単方向データフローコンセプト違反。
    • SSOT(Single Source Of Truthe)違反。
    • storeとコンポーネント内のdataの整合性の保証がない

FooPage.vue
<template>
  <ButtonFoo :name="name" @foo-click="getFoo()" />
</template>
<script>
import ButtonFoo from '@/components/ButtonFoo.vue'

export default {
  data() {
    return {
      name: ""
    }
  }
  computed: {
    foo() { return this.$store.getters.foo.name; },
  },
  methods: {
    ..mapActions('foo', ['getName'])
    getFoo().then {
      foo.getName().then((data) => {
        this.name = data
      })
    }
  }
}
</script>
store/modules/foo.js
import { getName } from '@/api/foo'

export default {
  namespaced: true,
  mutations: {
    FOO_NAME: (state, name) => {
      state.name = name
    }
  },
  actions: {
    return new Promise((resolve, reject) => {
      getName({ commit }): {
        asyncApi().then(response => {
          commit('FOO_NAME', response.data)
          resolve(response.data)
        }.catch((e) => {
          reject(e)
        })
      }
    }
  }
}
  • 合法化するには
    • actionsは、mutations にわたすデータを返り値にしない。
    • 同じ内容を保存するのは1箇所にする

※コードは1と同様なので割愛

脱法3: Atom/Moleculesがapiをダイレクトコール

  • 理由
    • アプリケーションデータを一元管理できない(どこで保存されているかわからなくなる)
    • APIとコンポーネントの結合が強くなると変更に弱い
    • APIのスキーマ変更の追従が困難になる

スキーマをそのままstateに利用しているコードも行儀は良くない。
パット見、独立したデータフローサイクルが構築されているので、問題ないように見えるコトがあるのが厄介。

Button.vue
<template>
  <button @click="onClick">{{ name }}</button>
</template>
<script>
import { getName } from '@/api/foo'

export default {
  data() {
    return {
      name: ""
    }
  },
  methods: {
    onClick() {
      getName().then((response) => {
        this.name = response.data
      })
    }
  }
}
</script>
  • 合法化するには
    • api呼び出しはactions経由に集約する。
      • APIのスキーマ変更に対しては、Vue.jsアプリケーション内のデータモデルとAPIのレスポンスモデルを別に定義しておくこと。OpenAPIを利用した自動生成を使うことで管理コストは削減観光。
    • (例外)アプリケーションのメインデータではないもの、ライブラリとして切り離せるもの。
      • 郵便番号から住所文字列をサジェストする再利用可能なOrganizmsなど
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?