Vue3のcomputedをちゃんと理解する|getter/setter・props連携のパターンまで

この記事で解決できるお悩み
  • Vue3の computed って何のためにあるの?
  • ref や普通の関数と何が違うの?使い分けがわからない
  • getter / setter の仕組みがよくわからない

computed って ref でも代替できそうだし、わざわざ使う必要あるの?」と感じている方も多いのではないでしょうか。しかし、computed を正しく使いこなすことで、コードの可読性・パフォーマンス・設計品質が格段に上がります。

この記事では、Vue3における computed の基本から getter/setter、さらには親子コンポーネント間の実践的な活用パターンまでを丁寧に解説します。読み終えたころには「computedをどこで使えばいいか」が明確にイメージできるはずです。

目次

computedとは

computed(算出プロパティ)とは、リアクティブなデータをもとに算出された値をキャッシュして返す機能です。

Vue3のComposition APIでは computed() 関数として提供されており、refreactive などのリアクティブな値が変化したときだけ再計算が行われます。

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('太郎')
const lastName = ref('山田')

// フルネームを算出
const fullName = computed(() => `${lastName.value} ${firstName.value}`)
</script>

<template>
  <p>{{ fullName }}</p> <!-- 山田 太郎 -->
</template>

fullNamefirstName または lastName が変わったときだけ再計算されます。それ以外のタイミングではキャッシュされた値を返すため、不要な計算処理が走りません。

変数・関数との違いと使い分け

通常の変数との違い

通常の変数はリアクティブではないため、依存する値が変わっても自動で更新されません。

<script setup>
import { ref } from 'vue'

const count = ref(0)

// ❌ リアクティブではない:countが変わっても更新されない
const doubled = count.value * 2

// ✅ computedを使えばリアクティブに追従する
import { computed } from 'vue'
const doubledComputed = computed(() => count.value * 2)
</script>

関数(メソッド)との違い

関数も同じ結果を返せますが、computed との決定的な違いはキャッシュの有無です。

<script setup>
import { ref, computed } from 'vue'

const count = ref(0)

// メソッド:呼び出されるたびに毎回計算が走る
const doubledByMethod = () => count.value * 2

// computed:依存する値(count)が変わったときだけ再計算
const doubledByComputed = computed(() => count.value * 2)
</script>

<template>
  <!-- テンプレートで複数回呼ばれる場合、メソッドは毎回計算される -->
  <p>{{ doubledByMethod() }}</p>
  <p>{{ doubledByMethod() }}</p>

  <!-- computedはキャッシュされるので2回目以降は計算不要 -->
  <p>{{ doubledByComputed }}</p>
  <p>{{ doubledByComputed }}</p>
</template>

まとめ:使い分けの基準

スクロールできます
通常の変数関数(メソッド)computed
リアクティブ✅(呼び出し時)✅(自動追従)
キャッシュ
向いているケース固定値副作用あり処理・引数が必要な処理リアクティブな値の加工・派生

こんなときは computed を選ぼう

  • リアクティブな値をもとに別の値を導き出したいとき
  • テンプレートで何度も参照する値を効率よく扱いたいとき
  • フィルタリング・ソート・文字列加工など「派生データ」を作りたいとき

こんなときは method を選ぼう

  • 引数が必要な処理
  • 非同期処理やDOMの操作など副作用を伴う処理

getter関数とsetter関数

getterだけのcomputed(デフォルト)

これまで紹介してきた computed(() => ...) の形式は、内部的には getter関数のみ を定義しています。getter は値を「読み取る」ときに呼ばれます。

<script setup>
import { ref, computed } from 'vue'

const price = ref(1000)
const tax = ref(0.1)

// getter のみのcomputed(読み取り専用)
const totalPrice = computed(() => Math.floor(price.value * (1 + tax.value)))
</script>

この場合、totalPrice.value = 2000 のように直接値を書き込もうとすると警告が出ます。

getter + setterのcomputed(書き込み可能)

computed にオブジェクト形式で getset を渡すことで、書き込みにも対応できます。

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('太郎')
const lastName = ref('山田')

const fullName = computed({
  // getter:値を読み取るときに呼ばれる
  get() {
    return `${lastName.value} ${firstName.value}`
  },
  // setter:値を書き込むときに呼ばれる
  set(newValue) {
    const parts = newValue.split(' ')
    lastName.value = parts[0]
    firstName.value = parts[1] ?? ''
  }
})
</script>

<template>
  <p>{{ fullName }}</p> <!-- 山田 太郎 -->
  <button @click="fullName = '鈴木 花子'">名前を変更</button>
  <!-- ボタンを押すとlastName='鈴木'、firstName='花子'に更新される -->
</template>

setter を活用することで、fullName という単一のインターフェースを通じて複数のリアクティブな値をまとめて更新できます。

活用例:親子間のフォームデータ連携

ユーザー情報編集フォームでよくある要件として、以下のようなケースがあります。

  • 親コンポーネントが初期値(姓・名など複数の値)を props で渡す
  • 子コンポーネントでユーザーが自由に編集できる
  • 変更された値を親に emit で通知する

今回は firstName(名)と lastName(姓)を別々の v-model で受け渡すパターンで実装します。

子コンポーネント

<script setup>
import { computed } from 'vue'

// 親からfirstNameとlastNameをそれぞれpropsで受け取る
const props = defineProps({
  firstName: {
    type: String,
    required: true
  },
  lastName: {
    type: String,
    required: true
  }
})

// 変更を親に通知するためのemit
const emit = defineEmits(['update:firstName', 'update:lastName'])

// 各propsに対してcomputedのgetter/setterを定義
const firstNameValue = computed({
  get() { return props.firstName },
  set(val) { emit('update:firstName', val) }
})

const lastNameValue = computed({
  get() { return props.lastName },
  set(val) { emit('update:lastName', val) }
})
</script>

<template>
  <div>
    <div>
      <label>姓</label>
      <input v-model="lastNameValue" type="text" placeholder="姓を入力" />
    </div>
    <div>
      <label>名</label>
      <input v-model="firstNameValue" type="text" placeholder="名を入力" />
    </div>
  </div>
</template>

親コンポーネント

<script setup>
import { ref } from 'vue'
import UserNameInput from './UserNameInput.vue'

const firstName = ref('太郎')
const lastName = ref('山田')
</script>

<template>
  <div>
    <h2>ユーザー情報編集</h2>

    <!--
      v-model:firstName="firstName" は内部的に以下と同等:
      :firstName="firstName" @update:firstName="firstName = $event"

      v-model:lastName="lastName" は内部的に以下と同等:
      :lastName="lastName" @update:lastName="lastName = $event"
    -->
    <UserNameInput
      v-model:firstName="firstName"
      v-model:lastName="lastName"
    />

    <p>現在の値:{{ lastName }} {{ firstName }}</p>
  </div>
</template>

props で受け取った値を直接 v-model に渡すと、Vueの単方向データフローの原則に反するため警告が出ます。しかし computed を介することで、propsは読み取り専用のままにしつつ、ユーザー入力のたびに emit で親に通知する設計が実現できます。

まとめ

最後まで読んでいただき、ありがとうございました!

今回は、「Vue3のcomputedの使い方」についてご紹介させていただきました。

computedリアクティブな派生データを作るための機能で、依存する値が変わったときだけ再計算されるキャッシュの仕組みを持っています。通常の変数や関数との違いを理解することで、どこで使うべきかの判断がしやすくなります。

また、getter/setterを組み合わせることで書き込みにも対応できv-model:xxx の引数付き記法と組み合わせれば、複数のpropsを持つフォームコンポーネントもスッキリ設計できます。

最初は「ref や関数で代替できるのでは?」と感じるかもしれませんが、一度 computed の使いどころを掴んでしまえば、テンプレートのロジックが整理されてコンポーネントの見通しが大きく向上します。ぜひ、あなたのVue3プロジェクトでも積極的に活用してみてください!

この記事が気に入ったら
フォローしてね!

シェアしていただけると大変励みになります!
  • URLをコピーしました!
  • URLをコピーしました!
目次