大ちゃんの駆け出し技術ブログ

RUNTEQ受講生のかわいいといわれるアウトプットブログ

【Vue】【自戒】computedとmethodsの相違点

過ち

こんにちは!大ちゃんの駆け出し技術ブログです。

今回はタイトルに【自戒】とあるように同じ過ちを繰り返さないために書いている記事になります笑

何をやらかしたのかというと、Vueファイルに下記のようなコードを書いた事です。

<template>
・
・
    {{ translateGender(profile.gender) }}
・
・
</template>

<script>
・
・
    methods: {
    translateGender(gender) {
      return gender == "male" ? "男性" : "女性";
    },
    },
・
・
</script>

profile.genderはサーバー側でenumで定義していました。

# app/models/profile.rb
class Profile < ApplicationRecord
  enum gender: { male: 0, female: 1 }

それにより、プロフィールをAPIで取得する時、返却されるJSONファイルのgenderの値はmale、もしくはfemaleでした。しかし、これを男性女性のいずれかでフロント側では表示したかったので下記のようにgenderの値がmaleの時(gender == "male")は男性を返却し、それ以外の値は女性と返却するメソッドを三項演算子を用いて定義しました。

translateGender(gender) {
  return gender == "male" ? "男性" : "女性";
},

実際こちらのコードは正しく動作し、translateGender(profile.gender)の値は男性もしくは女性と変換された形式で表示されます。

しかし、今回は以下のように処理は算出プロパティで定義すべきです。

<template>
・
・
    {{ translateGender }}
・
・
</template>

<script>
・
・
    computed: {
    translateGender() {
      return this.profile.gender == "male" ? "男性" : "女性";
    },
    },
・
・
</script>

問題点

何が今回のコードで問題だったのでしょうか。

こちらのコードの問題点は算出プロパティで定義しなかった事です。

上述したように男性女性の値に返却するtranslateGenderメソッドはmethodsで囲ってあります。

methods: {
  translateGender(gender) {
    return gender == "male" ? "男性" : "女性";
  },
},

しかし、今回自分がこちらの実装で行いたかったことは、既存データを加工するという実装です。このような既存のデータを加工して取得する機能をVue.jsでは算出プロパティで定義することがセオリーとなっています。

これは自分が通勤電車でVue.jsのテキストを読んでいたときにそのような記載がありました。メソッドと算出プロパティの違いが記載されている該当箇所をいかに抜粋します。

算出プロパティの用途は、基本的に既存データの「加工を伴う取得」です。一方、メソッドはデータの取得に加え、操作や更新にも利用できます。・・・<省略>・・・算出プロパティでできることはメソッドでもできます。ただし、引数を伴わない単純な加工や演算なのであれば、算出プロパティを利用した方がコードの意図が明確になります。

https://wings.msn.to/index.php/-/A-03/978-4-8156-0182-9/

このように、単純なデータの加工のみでデータを更新したりしない場合は、算出プロパティで定義するべきということです。

今回の件については、それほど大きな過ちではないのではないかと思ったのですが、同じテキストで算出プロパティとメソッドの明確な違いについて述べている箇所を読んで今回は算出プロパティにするべきだったと思いました。

そこで使用されている例のコードをそのまま使用させていただきます。(テキストの例はシングルファイルコンポーネントの形式ではないですが、、、)

<div id="app">
  <form>
    <input type="button" value="クリック" v-on:click="onclick" />
  </form>
  <div>算出プロパティ:{{ randomc }}</div>
  <div>メソッド:{{ randomm() }}</div>
  <div>現在日時:{{ current }}</div>
</div>
new Vue({
  el: '#app',
  data: {
    current: new Date().toLocaleString()
  },
  computed: {
    randomc: function() {
      return Math.random();
    }
  },
  methods: {
    onclick: function() {
      this.current = new Date().toLocaleString();
    },
    randomm: function() {
      return Math.random();
    }
  }
});

randomcは算出プロパティ

computed: {
  randomc: function() {
    return Math.random();
  }
},

randomm()はメソッド

methods: {
  randomm: function() {
    return Math.random();
  }
}

どちらも同じMath.random()でランダムな乱数を返却するという処理をしています。

表示される画面は以下のようになります。上の乱数部分が算出プロパティで下の乱数部分がメソッドです。

https://i.gyazo.com/6917dfbe9e650be4de8088cd56f3e0f1.png

v-on:click="onclick"によってイベントが発生するので、算出プロパティ、メソッドの値も変わることが期待されます。しかし、クリックを押すとメソッドの乱数の値は変わりますが、算出プロパティの値は変わりません。

https://i.gyazo.com/10412796e5c29551080e5fbedb0bf3ab.png

これこそが算出プロパティとメソッドの最大の違いです。

なぜこのようなことが起きるのかというと、メソッドは実行されるたびに定義されている処理を実行するのですが、算出プロパティでは依存プロパティに変更がない場合、過去に表示した値がキャッシュされていてその値を使用します。

今回自分が修正したコードを使用して説明すると、算出プロパティで使用しているthis.profile.genderの値がdataオブジェクト側で変更されない限り、前の処理結果をそのまま返却するということです!

computed: {
  translateGender() {
    return this.profile.gender == "male" ? "男性" : "女性";
  },
},

一見算出プロパティでも表示している内容は変わりませんが、それが処理を挟まずにキャッシュから表示したものと毎回処理を評価されるものでは、明らかにコードの処理の重さが違います。

そのため、単純な既存データの返却に関しては必ず算出プロパティを使うことを肝に銘じておこうと思いました。

今回は以上です!

参考記事

https://wings.msn.to/index.php/-/A-03/978-4-8156-0182-9/

https://qiita.com/yukibe/items/f49fcb935363200916fe