MicroAd Developers Blog

マイクロアドのエンジニアブログです。インフラ、開発、分析について発信していきます。

Vue.jsでのグローバル定数管理を工夫してみた話

システム開発本部フロントエンドエンジニアの工藤です。

マイクロアドの WEB アプリケーションのフロントエンドは、大半を Vue.js を用いて開発しています。 そこで、フロントエンドエンジニアとして効率の良いVue.jsの開発を目指し、日々奮闘しています。

今回は、Vue.jsでのグローバル定数管理についてお話していこうと思います。

はじめに

マイクロアドには様々なプロダクトがありますが、私の担当しているUNIVERSEというプロダクトについては、 フロントエンドフレームワークにVue.jsを利用し、SFCを採用しています。
Vue.jsやSFCについては、以前の記事を参考にするとより詳しく理解頂けると思います。

developers.microad.co.jp

また、props downevents upの思想にならいながらコンポーネントの細分化を行うことで開発効率上昇やUIの統一化を図っています。
その際コンポーネント化を難しくさせる要因として、下記がVueファイルの中に混在している点があると感じています。

  • テンプレート(HTML、 CSS)
  • 描画用データ(初期表示のデータやドメインデータなど)
  • 描画ロジック、 ビジネスロジック(computed、 methods、 その他ライフサイクル)

グローバル定数管理を見直したきっかけ

今回、特に注目したいのがテンプレートと描画データの部分になります。

templateとdataプロパティ

例えばこのような記述があったとします。

<template>
  <div>
    <div v-for="sample in samples" class="display-flex">
      <div class="width-100">ID :</div>
      <div class="text-align-right">{{ sample.id }}</div><!-- 数字だけ右寄せ -->
      <div class="width-100">名前 :</div>
      <div>{{ sample.name }}</div>
      <div class="width-100">ステータス :</div>
      <div>{{ sampleValue(sample.status) }}</div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      samples: [ // 本来はAPIから取得するデータ
        { id: 1, name: 'サンプルデータ', status: 'active' },
        { id: 2, name: 'サンプルデータ2', status: 'stop' },
        { id: 3, name: 'サンプルデータ3', status: 'stop' }
      ],
      sampleStatusView: { // ステータス状況によって出力するテキスト
        active: 'アクティブ',
        stop: '中止'
      }
    }
  },
  methods: {
    sampleValue(status) {
     return this.sampleStatusView[status]
    }
  }
}
</script>

statusが同ページ内で何度も使用され、孫コンポーネントやその先まで渡す必要がある場合、その間すべてにprops(sampleStatusView)を記載する必要が出てきます。

Vuexとの区分

こういった親子間以外でのデータの受け渡しや管理方法としてVuexが特に取り上げられると思います。 ですが、プロジェクトが大きくなってくるにつれて今度はVuexのstoreが肥大化するケースが見受けられるようになってきました。

そこで、データ管理方法の一環として、sampleStatusViewのような変化のない描画に必要なデータを別ファイルに切り出しディレクトリ管理することにしました。 (ここではconstディレクトリとします。)

Vuexとの使い分けは主にこのようにしています。

  • 定数やその定数に対する描画テキスト -> constディレクトリ配下
  • APIから取得してくるようなドメインデータ -> Vuex

グローバル定数の定義方法

今回の記事の本題になります。
まずは問題として挙げていた、sampleStatusViewの切り出しを行ってみます。

const SAMPLE_STATUS_VIEW = {
  active: 'アクティブ',
  stop: '停止'
}
<script>
import SAMPLE_STATUS_VIEW from '@/cost/sample.js'
export default {
  data() {
    return {
      samples:[]
      // sampleStatusViewは削除
    }
  },
  methods: {
    sampleValue(status) {
     return SAMPLE_STATUS_VIEW[status]
    }
  }
}   
</script>

dataプロパティから別ファイルに切り出されただけですが、これで孫コンポーネントやその先までpropsで渡さずとも、そのファイル内でimportして使用することができます。 ただ、この方法はあくまで定数にのみ適用することには注意が必要です。

応用

idnamestatusの表示部分がほぼ同様のDOM構造を繰り返し出力しています。idだけ右寄せにするためのCSSclassを付与することを描画用データとして持ち、constディレクトリ内で定義して活用をしてみます。

const SAMPLE_LABELS = {
  ID: 'id',
  NAME: 'name',
  STATUS: 'status'
}

const SAMPLE_LABEL_VIEWS  = [
  { property: SAMPLE_LABELS.ID, label: 'ID', isRight: true }, // idだけ右寄せCSSclassが付与される情報を持つ
  { property: SAMPLE_LABELS.NAME, label: '名前' },
  { property: SAMPLE_LABELS.STATUS label: 'ステータス' }
]

const SAMPLE_STATUS_VIEW = {
  active: 'アクティブ',
  stop: '停止'
}
<template>
  <div>
    <div v-for="sample in samples" class="display-flex">
      <template v-for="SAMPLE_LABEL_VIEW in SAMPLE_LABEL_VIEWS">
        <div class="width-100">{{ SAMPLE_LABEL_VIEW.label }} :</div>
        <div :class="{ 'text-align-right': SAMPLE_LABEL_VIEW.isRight }"><!-- 数字だけ右寄せ -->
          {{ sampleValue(sample, SAMPLE_LABEL_VIEW) }}
        </div>
      </template>
    </div>
  </div>
</template>
<script>
import { SAMPLE_LABELS, SAMPLE_LABEL_VIEWS, SAMPLE_STATUS_VIEW  } from '@/cost/sample.js'
export default {
  data() {
    return {
      samples:[]
      // sampleStatusViewは削除
    }
  },
  computed: {
    SAMPLE_LABEL_VIEWS() {
      return SAMPLE_LABEL_VIEWS
    }
  },
  methods: {
    sampleValue(sample, SAMPLE_LABEL_VIEW) {
     const property = SAMPLE_STATUS_VIEW.property
     return property === SAMPLE_LABELS.STATUS
        ? SAMPLE_STATUS_VIEW[sample[property]]
        : sample[property]
    }
  }
}   
</script>

importした SAMPLE_LABEL_VIEWS をtemplateで使用するためには一度、Vueインスタンスに含める必要があります。そこで参照のみであることも踏まえ、computedに定義することでtemplateで活用しています。

SAMPLE_LABEL_VIEWS.label に関しては、もうお気づきかもしれませんが、本来サーバーサイドで行われることの多いenum定義をフロントで行っていることになります。 そして、そのデータと共に描画用情報(今回はCSSclassを付与するかどうか)を持つことでtemplateを簡略化しています。

あくまで定数をjsファイルに切り出し、グローバルデータとして管理することでバケツリレーを防ぐのがメインですが、描画用情報も管理することでデータ管理を楽にしています。

終わりに

constディレクトリによるグローバル定数化を導入して、コンポーネントと描画用データの分離まで行ったことにより、開発効率が向上したように感じています。 ですがこれで終わりではなく、下記の展望も見えています。

const、 viewの使い分け

定数と描画用情報(今回で言うところのisRight)を厳密に分けることも考えれますが、不用意に複雑になる可能性があり、検討中です。

jestによるテストへの活用

テストには、テストデータが必要になってきます。例えば、status に アクティブか停止が必ず表示されているのかをテストするならば、SAMPLE_STATUS_VIEW を活用することが出来そうだと思います。

developers.microad.co.jp

マイクロアドでは、フロントエンドエンジニアを募集しています!また、サーバサイド、機械学習、インフラエンジニアなど幅広く募集していますので気になった方は以下からご応募ください! recruit.microad.co.jp

少しでも参考になれば幸いです、ご覧いただきありがとうございました。