【Vue.js】税金等の計算シミュレーションを作っていて気づいた4点

私はjavascriptに関してはjQueryをやっていただけで、とくにJSのフレームワークは触ったことありません。
Vue.jsがはじめて触ったjavascriptのフレームワークでした。
そんな私がVue.jsで作成しているときに疑問に思った点などを上げます。

環境

vue 2.5.17

vue.jsで迷った実装方法

定数の宣言場所・方法

税率等の値とか定数は管理しやすいようにまとめますよね。一体どのように実装していくのがベストか?よくわかりませんでした。
それぞれの設定値を別ファイルで管理するのは当然として。

結論 mixinを使う

Qiitaでこの方法を使っている方の記事を見かけて、こちらの方法を採用。
API経由で取得したいとかでてきたら、この部分を回収すれば行けそうだったので。

const taxes = [
  {'id': 1, 'from': 0, 'to': 195, 'tax_rate': 5, 'deduction': 0},
  {'id': 2, 'from': 196, 'to': 330, 'tax_rate': 10, 'deduction': 97500}
]

export default {
  data () {
    return {
      taxes: taxes
    }
  }
}
</code></pre><p>使用方法</p>
<pre><code>import taxes from './taxs'

export default {
  mixins: [ taxes ],
  methods: {
    getTaxes() {
      return taxes
    }
  }
}

dataオプションと算出プロパティ(computed) どちらを使うか

リアクティブデータを使おうとした際に、はじめはdataで定義していましたが後々computedの方が都合がいいなぁと移動することが良くありました。 途中からどちらで定義した方がいいかを迷うようになってきました。

例)収入の入力部
収入は最初の設計から「収入」の入力か1~12月の月毎の収入を入力し、足した値を「収入」に設定する2パターン考えていました。
でまずは、computedを使ったのですが、その際テンプレート分けしていなかったので以下のようなコード

初期実装

<template>
  <div>
    <!– 収入 –>
    <input type="number" v-model="total">
    <!– 月毎 –>
    <div v-for="item in months" :key="item.month">
      <input type="number" v-model.number="item.value">
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    total: function() {
      return this.months.reduce((prev, current) => { return { 'value': prev.value + current.value } }).value
    }
  }
}
</script>

当初はこれでよかったのですが、月毎の入力を別テンプレート化した際にdataオプションへと移動になりました。
子→親に値を受け取る形になるので算出プロパティではなくなりました。

変更後

収入部(親)

<template>
  <div>
  <!– 収入 –>
  <input type="number" v-model="total">
  <!– 月毎 –>
  <DetailForm v-on:onChangeIncomeMonthsTotal="onChangeIncomeMonths"/>
  </div>
</template>

<script>
import Detail from ''
export default {
  mame: 'InputForm',
  components: { DetailForm },
  data: function () {
    return {
      total: 0,
    }
  },
  methods: {
      onChangeIncomeMonths: function(total) {
        this.total = total
      }
    }
  }
}
</script>
</code></pre><p>月毎(子)</p>
<pre><code><template>
  <div>
    <div v-for="item in months" :key="item.month">
      <input type="number" v-model.number="item.value">
    </div>
  </div>
</template>
<script>

export default {
  name: 'InputDetailForm',
  data() {
    return {
      months: [
        {month: 1, value: 0 },
        ・・・12月まで
      ]
    }
  },
  computed: {
    total: function () {
      const sum = this.months.reduce((prev, current) => { return { 'value': prev.value + current.value } }).value
      // イベント発火
      this.$emit('onChangeIncomeMonthsTotal', sum)
      return sum
    }
  }
}

まぁ、最初から設計しとけよ!という話ですが、リファクタリングしていくと持ち方自体変更しないといけない場合も多々ありました。ここは徐々に慣れていくしかないかなぁという感触。

テンプレートコンポーネント化時のデータ受け渡し

jQueryはDOMから直接取得しますが、Vueでは基本リアクティブデータを使用します。
コンポーネント間はprops, emitで親子間のデータ受け渡しが基本なのですが項目が増えれば増えるほど大変に。。
一部のテンプレートではこんなことに。。

<ChildComponent
    v-on:onChangeChildValue1="onChangeValue1"
    v-on:onChangeChildValue2="onChangeValue2"
    v-on:onChangeChildValue3="onChangeValue3"
    v-on:onChangeChildValue4="onChangeValue4"
    v-on:onChangeChildValue5="onChangeValue5"
    />

受け渡しする値をオブジェクトにしてしまうとか方法もありますが、最初はこんなことになってました。頭がくらっとしますね。
うん、最初からVuex使おう!

title, meta属性、navbarなどの実装

エントリーポイントとなるindex.htmlはhtmlしか記述できないので、動的に変更したりとかリアクティブデータを使った更新とかどうやるのがベストなのか。。vue-routerを使えば解決するようですがこのレベルのアプリでは使っていないですし、当初navbarはindex.htmlに設置してました。
結局DOMを書き換える方法になりましたが。。(watchでリアクティブデータを監視し、変更されたら反映するみたいな感じ)

最後は、極力index.htmlにロジックが絡むものは書かないよう、html構成自体変更しました。(おそらくこれが正しい)

まとめ

Vue.jsは触ってて大変おもしろいフレームワークですね。自由度がとても高いので最初は試行錯誤がありそうですが、慣れると簡単にWebアプリが作成できそうです。

今回参考にさせていただいた本です。とても分かりやすく、イラストもとてもかわいいのでおすすめです。