【可読性をあげろ!dataは極力駆逐する!】Vue初心者のためのcomputed入門


Vueを始めたばかりの方へ

今年はVue(Nuxt)の学習を始めたり、プロジェクトで使用された方も多そうですね!
簡単にリアクティブな要素の構築が行えるため、実装が楽しく学習も捗っているのではないでしょうか?

Vueは学習コストが低く、導入しやすく、すぐ可視化されるため、非常に使いやすいという印象を受けますが
それとは裏腹に 「処理が複雑化すると可読性が一気に落ちてしまう」 印象を持っています。

そこで!
今回は個人的にレビュー時に指摘する事の多かった 「computedの使い方」について書いています。
ほぼリファレンスや他の方も書いているものと同様ですが、自分なりにまとめただけです。
初心者向けではありますが、Vueを使っている方もぜひ初心に振り替える気持ちで一度目を通してみてください。

読み方


重要なことだけ教えろって方は、大項目と強調文字だけ読んでください。

対象者

  • Vueを始めたばかりの方
  • これからVueを始めようと思っている方

目的

  • 読む側にとって負担のないコードを書く。
  • より良い書き方を理解する。

注意点

  • あくまでも、個人的意見です。(6ヶ月程度をぼちぼちやった程度なので、変なところありましたらマサカリ投げてください)
  • 業務でコーディングする際は、プロジェクトのコーディング規約に従ってください。

Computedについて

リアクティブな要素(dataやprops)を使用してcomputedを定義すると、要素に変化がある場合にcomputedも再計算されます。
下記のようにdataにradius(半径)を定義しUIに紐づけて書き換えると、それに応じてcircleArea(円面積)が再計算されます。
またこの circleAreaの結果はキャッシュされるため、radiusが変化しない限り基本的に再計算されません。

円面積を求める事の出来るComponent

export default {
    data() {
        return {
            radius: 0 // UI等に紐づけて書き換えるようにする。
        }
    },
    computed: {
        circleArea() {
            return Math.pow(this.radius, 2) * Math.PI
        }
    }
}

Vueはリファレンスが読みやすいため、こちらでも理解できると思います。
https://jp.vuejs.org/v2/guide/computed.html

こんなの当たり前でしょ?と思いがちですが、慣れはじめは難しさが分からないですし、慣れてから難しさを実感します。
(computedがRxのcombineLatestのような合成の役割を担っている事に気づいたのは意外と最近でした…)

このように「変化」すると「動作を行う」… これは リアクティブプログラミング と呼ばれています。
Excelを例とした説明をよく見ますので、リアクティブプログラミングとはなんぞや?という方は探してみてください。
Vueは簡単といわれますが、リアクティブプログラミングの知識や経験が乏しいと複雑な仕様を実装する際に悪手を踏みがちです。

では、どのような使用方法があるのかを紹介しようと思います。

定数

computedに定数等の変化しない値を入れてしまうパターンです。
dataやpropsのようなリアクティブな要素を保持していない場合は、計算されたのちキャッシュされるだけになるため
その点を利用したものです。

サンプル

とりあえず例を見てみましょう。
ゆとり円周率を使って円の面積を求める場合です。
この程度であれば、computedを使うまでもありませんが、複雑化するとこの書き方が生きてきます。

dataを使ったコード
export default {
    data() {
        return {
            yutoriPi: 3, // 定数であっても、間違って書き換えてしまっても動く。
            radius: 0
        }
    },
    computed: {
        circleArea() {
            return Math.pow(this.radius, 2) * this.yutoriPi
        }
    }
}
computedを使ったコード
export default {
    computed: {
        yutoriPi() {
            return 3
        },
        circleArea() {
            return Math.pow(this.radius, 2) * this.yutoriPi
        }
    }
}

定数をcomputedで書くメリット

computedに書くことで、明示的に定数は上書きすることができません。
初心者の方は定数をdataに書きがちですが、dataは基本的に書き換える前提のものだけを定義する事で
「このコンポーネントではdataにあるものだけが書き換わるんだ」 という前提でコードを読む事ができ
可読性を上げる事で不具合を減らす事が可能です。

監視(値を再計算)

computedは中で使用しているリアクティブな要素が変更された場合、検知して再計算します。
初心者の方はwatchを使いたいと感じてしまうと思いますが、watchよりスマートに書け一時変数を作る事なく可読性を上げられます。

サンプル

単に四角の面積を計算し表示するだけのためのcomponentです。

watchを使ったコード
export default {
    data() {
        return {
            width: 0,
            height: 0,
            rectangleArea: 0 // これを表示したいUIに紐づける
        }
    },
    watch: {
        width: function() {
            this.updateRectanbleArea()
        },
        height: function() {
            this.updateRectanbleArea()
        },
    },
    methods: {
        updateRectangleArea() {
            this.rectangleArea = this.width * this.height
        }
    }
}
computedを使ったコード
export default {
    data() {
        return {
            width: 0,
            height: 0
        }
    },
    computed: {
        rectangleArea() { // これを表示したいUIに紐づける
            return this.width * this.height
        }
    }
}

computedで書くメリット

dataにあった一時変数が消えてすっきりしました。
またwatchで2つの要素を監視し同じメソッドを呼んでいましたがなくなりました。
computedには 一時変数を消してリアクティブなプロパティ(キャッシュできる点)のように書ける というメリットがあります。
$watch関数を使っても複雑化は変わらないため、computedで書く方が好ましいです。

まとめ

まずdataを駆逐しろ!そのためにcomputedを駆使する!

Vueの処理が煩雑な場合はcomponentに分けるのも良いと思いますが
まずは 不要な一時変数などを使用していないか?computedですっきりと書き換える事ができないか? を考えましょう。
コードの整理が行えていない状態でcomponentに切り出しても不自然な作りになるだけ だと個人的には思っています。

ぜひcomputedがどのような動きをするのか?を検証するコードを書いてみて体感することをオススメします。

紹介したものを使ったサンプル

12/18 までに更新予定(フィルターをかけるようなサンプルコード)

後書き

アドベントカレンダーに参加するぞ!!と張り切っていましたが、体調不良やプロジェクトで投稿がこんな時間に…(誤字脱字多そう。)

Vueは非常にとっつきやすく良いと思いますが、リアクティブプログラミングを理解していないと
悪手を踏んで解決しにくい不具合が発生するのでは…?と懸念しています。(データの変化の伝達が把握できない程に複雑化するとか)
少しでも初心者の方にcomputedの便利さを伝えられ、しっかりこの書き方でコーディングできる方が増える事を願ってます!