こんにちは、ラボルの@naoki_m05です。
ラボルでは、フロントエンドにVue.jsを使用しているのですが、コードレビューにて「ここはdataじゃなくてcomputedにした方がいいよ」という指摘がこれまでで10回以上ありました。
suggestion getCurrentYear()はcomputedで書き換えたほうが、記述も減るし勝手に算出してくれるのでいいですよ
question: これって変更できていいんですか?変更できちゃだめな気がするのでcomputedがしっくりきます。
nitpick: flag系のdataはなるべく増やさないって感じじゃなかったですっけ?computed使えますよ
などなど
レビュアーの負荷を減らすために以前社内記事を書いたのですが、今回テックブログ上でも共有したいと思います。何らかの貢献があれば嬉しいです。
目次
Vue.jsのコーディングスタイルについて、本体価格から税込価格を計算するプログラムとそれを修正していく過程を題材にしてjQueryと比較しながら説明します。
次に本題である、dataではなくcomputedを使った方が良いという説明をします。
1. Vue.jsのコーディングスタイル
1-1. 税込価格を計算するプログラムのコード例
本体価格を入力すると税込価格を計算し表示するプログラムをコード例として説明します。 (ChatGPTで生成したコードの一部を載せています。ChatGPT便利ですよね。)
1-1-1. jQuery
HTML
<label for="price">本体価格:</label> <input type="text" id="price"> <p id="tax-included-price"></p>
Event Handler
$(document).ready(function() { $('#price').on('input', function() { var price = parseFloat($(this).val()); if (!isNaN(price)) { var tax = price * 0.1; // 税率は10% var taxIncludedPrice = price + tax; $('#tax-included-price').text('税込価格: ' + taxIncludedPrice.toLocaleString() + '円'); } else { $('#tax-included-price').text(''); } }); });
1-1-2. Vue.js
template
<div id="app"> <label for="price">本体価格:</label> <input type="text" id="price" v-model="price"> <p>{{ taxIncludedPrice }}</p> </div>
computed
data(){ return { price: 0 } }, computed: { taxIncludedPrice: function() { if (!isNaN(this.price)) { var tax = this.price * 0.1; // 税率は10% var taxIncludedPrice = this.price + tax; return '税込価格: ' + taxIncludedPrice.toLocaleString() + '円'; } else { return ''; } } }
現段階では、jQueryのコードもVue.jsのコードも本体価格を入力すれば、税込価格が表示され、機能的には同じです。
1-2. 機能追加時の修正過程
機能追加に伴いコードを修正していきます。 jQueryとVue.jsそれぞれのスタイルの違いを際立たせていきます。
1-2-1. 税込価格の表示箇所を増やす
税込価格の表示箇所を増やす場合は、それぞれ下記の修正が必要です。
- jQuery: HTMLに新たなpタグ(idは#tax-included-price2?)を追加し、EventHandler内に
$('#tax-included-price2').text = ~
を追加する - Vue.js: templateに
<p>{{ taxIncludedPrice }}</p>
を追加する。
1-2-2. 消費税率10%だけでなく、5%や8%にも対応する
セレクトボックスを使用し、[0.05, 0.08, 0.10]
のうち1つを選択できるように修正します。
- jQuery: HTMLにセレクトボックスを追加し、既存EventHandlerに選択した税率を考慮した計算ロジックになるよう変更を加える。 セレクトボックスのchangeイベントを監視するEventHandlerを追加する(この新たなHandlerの中でも計算処理とDOM更新処理が必要)。
- Vue.js: templateにセレクトボックスを追加し、選択した税率(taxRate)をdataで管理し、computed内ではthis.taxRateを用いて計算処理を行うよう修正する。
1-3. 関心の方向とコーディングスタイル
1-2-1の変更の際、jQueryの場合は表示のUIの方にも関心を持ってしまっているため、新たなpタグの追加にEvent Handlerが影響を受けてしまいます。
一般的に関心を持てば持つほど、責務が増えコードが複雑になってしまいます。また、関心先のコードが変われば自身に変更が及んでしまいます。
一方Vue.jsはcomputedはUIに関心を持っておらず、UIに変更があってもcomputedに影響はありません。ただただtemplateを変更するだけで済みます。
また、1-2-2の変更の際、jQueryの場合は、税率の選択と本体価格の入力の2つのイベントを監視するEventHandlerが必要になってしまいます。また、それぞれで税込価格の算出処理が必要になります。([計算]ボタンを用意しクリックイベントがあれば計算して表示するという方法であればEvent Handlerは1つになりますがUXが悪化する可能性があります。)
黒い矢印は関心の向きを表しています。jQueryはEvent Handlerが増えやすく、入力系にも表示UIにも関心を持っているため複雑になりやすいです。一方で、Vue.jsはそうではありません。
Vue.jsはリアクティブシステムを採用しており、データの変更が自動で反映されます。言い換えると、コード上関心がなくても変更が入力系からUIへと反映されていきます。
このようにdataやcomputedを使用するコーディングスタイルを採用すれば、修正を重ねても関心を減らすことができ複雑さも逓減できます。
2. dataかcomputedか
それでは、ここからdataを使うべきかcomputedを使うべきかの話に移ります。
仮にAPIをリクエストをして①数十件ほどの会員の一覧データを取得し、②その会員の一覧と会員数に表示する例を考える場合、下記の2通りの方法があります。
どちらも同じですが、もし、listに更新があった場合、countの更新も同時にする必要があります。
例えば、このようにAPI通信で取得した際にも同時に変更を加える必要がありますし、
axios.get("/member-list").then(res=>{ this.list = res.data this.count = res.data.size() })
仮にどこかでthis.list.push(hoge)
をする際、同時にthis.count++
もする必要があります。
これでは開発者の考える量が増えてしまい、もっと複雑なコードになってくるとバグが発生する可能性も増えることでしょう。
基本的に関心はcomputedに対して向け、データの反映の流れについては段々と算出(導出の方が近い気もする)されていくような感覚を持つと良いと思います。
そのため、できるのであればcomputedを使うと良いのです。
最後に
ラボルでは、エンジニアを積極採用中です。1、2年目のエンジニアから経験豊富なテックリードやエンジニリングマネージャーまで、興味がある方はぜひご応募ください!!