kz’s blog

興味のあることについて書いていきます。

Vue.js の methods と computed の違いを検証して理解する

はじめに

jp.vuejs.org

公式の算出プロパティ(computed)を参照したところ、あまりピンと来なかったので、検証をして理解を深めました。

methods と computed の違い

キャッシュされるかされないか

  • キャッシュされる  =computed
  • キャッシュされない =methods

何を言っているのかよく分からないので検証してみました。

取り敢えず以下のアプリを用意

テンプレート

<template>
  <div>
    <div>
      <input type="text" v-model="text"/>
    </div>
    <div>
      computed: {{reversedMessageComputed}}
    </div>
    <div>
      methods: {{reversedMessageMethods()}}
    </div>
    <div>
    <button @click="incrementCount()">count: {{count}}</button>
    </div>
  </div>
</template>

スクリプト

<script>
export default {
  data () {
    return {
      text: 'input text',
      count: 0,
    }
  },
  computed: {
    reversedMessageComputed () {
      console.log('computed:' + this.text)
      return this.text.split('').reverse().join('')
    }
  },
  methods: {
    reversedMessageMethods () {
      console.log('methods:' + this.text)
      return this.text.split('').reverse().join('')
    },
    incrementCount () {
      this.count++
    }
  }
}
</script>

テキスト入力欄と入力したテキストを反転させて表示させる領域を用意しました。

反転させる部分は、computed, methodsでそれぞれ呼び出しています。

そして、computed, methodsそれぞれでいつ呼ばれたか分かるようにコンソールを出力しています。

またボタンを用意し、クリックするとボタンに表示しているcountが増えていきます。

画面表示

画面はこのようになります。

初期画面

f:id:kz62:20181020090021p:plain

テキスト入力後

テキストに文字を打つと、反転表示部分が更新され、それぞれのコンソールが出力されます。

f:id:kz62:20181020090424p:plain

f:id:kz62:20181020090615p:plain

テキストを変更したので、computed, methodsのどちらも依存関係にあるので実行されているのがわかります。

ではボタンを押すとどうなるでしょうか。

ボタン押下後

ボタンを押下するとcountが更新されます。

f:id:kz62:20181020094311p:plain

f:id:kz62:20181020094317p:plain

コンソールを見ているとmethodsの方だけ出力されていることがわかります。

このように、methodsは依存関係にないプロパティが変更された場合でも実行されてしまうことが分かりました。

computed はプロパティである

computedは算出プロパティと呼ばれているようにプロパティの一種です。

そしてgetter, setterを分けることで以下のようなことができるようになります。

getter, setterを意識しない場合、getterだけになります。

テンプレート

<template>
  <div>
    <div>
      {{text}} <!-- 変更 -->
    </div>
    <div>
      computed: <input type="text" v-model="reversedMessageComputed"/> <!-- 変更 -->
    </div>
    <div>
      methods: {{reversedMessageMethods()}}
    </div>
    <div>
    <button @click="incrementCount()">count: {{count}}</button>
    </div>
  </div>
</template>

スクリプト

<script>
export default {
  data () {
    return {
      text: 'input text',
      count: 0,
    }
  },
  computed: {
    // 変更(getter, setter に分ける)
    reversedMessageComputed: {
      get () {
        console.log('computed(get):' + this.text)
        return this.text.split('').reverse().join('')
      },
      set (newValue) {
        console.log('computed(set):' + this.text)
        this.text = newValue.split('').reverse().join('')
      }
    }
  },
  methods: {
    reversedMessageMethods () {
      console.log('methods:' + this.text)
      return this.text.split('').reverse().join('')
    },
    incrementCount () {
      this.count++
    }
  }
}
</script>

先ほどからの変更点は、テキストのv-modelreversedMessageComputedにし、textプロパティは表示のみさせるようにしました。

また、前述の通り、computedgetter, setterに分けております。

画面表示

f:id:kz62:20181020095956p:plain

テキスト入力後

f:id:kz62:20181020100051p:plain

reversedMessageComputedsetterが動作してtextプロパティも変更されました。

ただし、computedはあくまでプロパティでありファンクションではないので、引数を渡すことはできないみたいです。

2018/10/30 追記

computedでも引数を渡す書き方があったので追記しておきます。

<template>
  <div>
    <!-- function と同じように指定する -->
    argumentComputed: {{argumentComputed('arg')}}
  </div>
</template>
<script>
export default {
  data () {
    return {
      text: 'input text',
    }
  },
  computed: {
    argumentComputed () {
      return value => {  // function をリターンする
        console.log('computed(arg):' + this.text)
        return this.text + value;
      }
    }
  },
}
</script>

ただし、このようにするとmethodsと同じ振る舞いになってしまうようで、依存していないプロパティの更新でも実行されてしまいました。

おわりに

まとめると、

methods : キャッシュされない、ファンクション

computed : キャッシュされる、プロパティ

特に、キャッシュの部分は画面のパフォーマンスを飛躍的に向上させることができる場合もあるので、使い分けが非常に重要だと感じました。