これは「「はじめに」の 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に集約させる
- Atom/Moleculesへ値を渡すときは
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の整合性の保証がない
- Vuexの
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など
-