「frontend(vue)をリファクタリングした話」 わたしのWebサービス開発日記 #24

スポンサーリンク
個人開発
スポンサーリンク

はじめに

いままで、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つの子コンポーネントに分けることで、コードの可読性が上がりました。

どこに何の機能が書いてあるかわかるからです。

これで一つのファイルのコードが肥大化していくのを防ぐことができました。

タイトルとURLをコピーしました