はじめに
いままで、App.vueが600行以上ありました。
vueの内容は全てApp.vueに書いていました。
600行以上あると、templateとscriptとstyleでどの変数がどこで使うものなのかがぐちゃぐちゃになりがちで、同様の名前の変数などが入り乱れることになります。
実際、なり始めていました。
そこで、今回はvueのリファクタリングをすることにしました。
その記録を残します。
リファクタリング前のsrcのディレクトリ構成は以下。
$ tree -L 2
.
├── App.vue
├── assets
│ ├── images
│ ├── logo.png
│ └── style
└── main.js
分割箇所について
分割箇所は以下の3つのエリアで分けて分割しました。
分割後
分割後のディレクトリ構成は以下になりました。
$ tree -L 2
.
├── App.vue
├── assets
│ ├── images
│ └── style
├── components
│ ├── Header.vue
│ ├── JapanMap.vue
│ └── OtherFunction.vue
├── main.js
└── router
└── index.js
リファクタリングするときのポイント
ポイント①:小さいところから変更していく
一気に変えようとすると逆に時間がかかります。
コードが少ないところから1つずつ子コンポーネントにして、動くのを確認したら、次のコンポーネントを作っていくという流れが良いです。
ポイント②:コンポーネント間をまたぐ変数について
ここが一番、てこずりました。
というかこの事を備忘として残すためにこの記事を書きました。
てこずったのは、子コンポーネントAで定義した変数の値を子コンポーネントBで使用したいときです。
これについてはサンプルプログラムを用意しておきます。(コードはChatGPTに書いてもらいましたので、ChatGPTに聞いた方が早いかも)
✅子コンポーネントB
<template>
<div>
<label v-for="item in items" :key="item.id">
<input type="checkbox" :value="item" v-model="selectedItems"> {{ item.name }}
</label>
</div>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true
}
},
data() {
return {
selectedItems: []
};
},
watch: {
selectedItems(newVal) {
this.$emit('update:selectedItems', newVal);
}
}
};
</script>
チェックボックスの値が変更されたらselectedItemsが値を更新して、$emitを使って親側にselectedItemsの値が送られます。
✅親コンポーネント
<template>
<div>
<ChildComponentB :items="items" @update:selectedItems="handleUpdateSelectedItems" />
<ChildComponentA :selectedItems="selectedItems" />
</div>
</template>
<script>
import ChildComponentA from './ChildComponentA.vue';
import ChildComponentB from './ChildComponentB.vue';
export default {
components: {
ChildComponentA,
ChildComponentB
},
data() {
return {
items: [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
],
selectedItems: []
};
},
methods: {
handleUpdateSelectedItems(newSelectedItems) {
this.selectedItems = newSelectedItems;
}
}
};
</script>
親コンポーネントでは、子コンポーネントBの値が更新されたら、
<ChildComponentB :items="items" @update:selectedItems="handleUpdateSelectedItems" />
で、
handleUpdateSelectedItems(newSelectedItems) { this.selectedItems = newSelectedItems; }
が起動し、selectedItemsに更新された値が入ります。
そして、
<ChildComponentA :selectedItems="selectedItems" />
で更新されたselectedItemsが子コンポーネントAに送られるという訳です。
✅子コンポーネントA
<template>
<div>
<button @click="handleButtonClick">Get Selected Items</button>
<ul>
<li v-for="item in selectedItems" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
export default {
props: {
selectedItems: {
type: Array,
required: true
}
},
methods: {
handleButtonClick() {
// ボタンクリック時に選択された項目を使用
console.log('Selected Items:', this.selectedItems);
// 必要な処理をここに実装
}
}
};
</script>
子コンポーネントAでは
props: { selectedItems: { type: Array, required: true } },
で値を受け取ります。
リファクタリング後
一つのvueファイルを、3つの子コンポーネントに分けることで、コードの可読性が上がりました。
どこに何の機能が書いてあるかわかるからです。
これで一つのファイルのコードが肥大化していくのを防ぐことができました。