はじめに
社内システムのフロントエンドにVue.jsを利用しています。
コンポーネント間でデータのやり取りをpropsやemitでやっていたのですが、
あまりにもそれらの量が多くなりコンポーネントどうしが密結合してしまいました。
状態はVuexで管理するとして、
「ダイアログの開閉動作もなんとか分離できないか?」
ということで、modelプロパティを利用して疎結合にしました。
やること
modelプロパティを使う場合と使わない場合の、ダイアログの開閉動作の実装の違いを見て、
同プロパティが疎結合に寄与していることを確認します。
ダイアログは、VuetifyのVDialogを利用し独自に定義したコンポーネントを使うといった想定です。
※前提知識として、Vue.jsでは親の値を子が直接変更すると、動きはするものの怒られるということを知っておいてください。
環境
今回実行した環境は以下です。
- Vue.js 2.6.12
- Vuetify 2.4.3
ソースコード
1. modelプロパティを使わない場合
Parent.vue
<template>
<div>
<v-btn depressed @click="openDialog">
Open
</v-btn>
<dialog :value="value" @close="close">
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import dialog from '@/dialog';
export default Vue.extend({
name: "Parent",
data: {
value: false
},
methods: {
openDialog() {
this.value = true;
},
close() {
this.value = false;
}
}
})
</script>
Dialog.vue
<template>
<div>
<v-dialog v-model="value">
<v-card tile>
hoge
</v-card>
</v-dialog>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: "Dialog",
props: {
value: {
type: Boolean,
default: false
}
},
methods: {
close() {
this.$emit("close", false);
}
}
})
</script>
2. modelプロパティを使う場合
Parent.vue
<template>
<div>
<v-btn depressed @click="openDialog">
Open
</v-btn>
<dialog v-model="value">
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import dialog from '@/dialog';
export default Vue.extend({
name: "Parent",
data() {
value: false
},
methods: {
openDialog() {
this.value= true;
}
}
})
</script>
Dialog.vue
<template>
<div>
<v-dialog
:value="value"
@input="(val) => val || close()"
>
<v-card tile>
hoge
</v-card>
</v-dialog>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: "Dialog",
model: {
prop: "value",
event: "change-dialog"
},
props: {
value: {
type: Boolean,
default: false
}
},
methods: {
close() {
this.$emit("change-dialog", false);
}
}
})
</script>
何が変わったか
1. modelプロパティを使わない場合
やっていること
- propsでvalue変数(ダイアログの開閉状態)を渡している
- エラーを避けるため、ダイアログを閉じる処理はParent.vueが担っている
良くない点
- 処理がParent.vueに依存する部分があるため、コンポーネントが密結合している
- 閉じるためにモーダル部分をクリックすると、Dialog.vueがParent.vueのvalue変数を直接書き換えるためエラーとなってしまう
- Dialogコンポーネントを使いまわそうとすると、そのたびに親にダイアログを閉じる処理を書かないといけない
2. modelプロパティを使う場合
やっていること
- v-modelを経由して、ダイアログの開閉状態をDialog.vueに渡している
- v-modelで渡した状態をmodelプロパティ(とprops)で受け取っている
- Dialog.vueの
@input
イベントでダイアログの開閉状態を監視している
良い点
- 処理がDialog.vueで完結しているため、1に比べてコンポーネントが疎結合になっている
- モーダル部分をクリックして閉じたとしても、エラーとならない
- 親コンポーネントが簡素になる
最後に
modelプロパティを使うことでコンポーネントを疎結合にすることが出来ました。
子コンポーネントにmodelプロパティとprops両方を書かないといけず、記述量が増えるのが難点ですが
疎結合になることで使いまわすことが容易になりました。