404 Not Found

404 Not Found


nginx

Vue 3 の計算プロパティ:計算結果のキャッシュと依存関係の追跡

計算プロパティ(computed)は、Vue 3における特別なプロパティであり、リアクティブなデータに基づいて自動的に再計算されます。その最大の利点はキャッシュ機能にあります。つまり、依存するデータが変更された場合にのみ再計算され、それ以外の場合は単に以前の値を返します。計算プロパティを理解することは、高性能なVueアプリケーションを作成するための鍵となります。

計算プロパティの中核をなすのは、自動依存関係追跡+結果のキャッシュ+リアクティブ性という3つの主要な機能です。このレッスンでは、その5つのユースケースを習得し、3つの落とし穴を回避する方法を学びます。

1. 学習内容


2. ショッピングカートの合計金額に関するパフォーマンスの問題

(1) 課題:クリックするたびに100個の商品の再計算が行われる

アリスは、自身のECサイトの管理画面にカートの合計金額計算機能を構築しました:

JS
// ❌ The "Flip" Version: Use methods
function cartTotal(items) {
  console.log('Calculating...')  // Print on every call
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
HTML
<template>
  <div>
    <p>Total: ${{ cartTotal(items) }}</p>
    <p>Tax: ${{ cartTotal(items) * 0.08 }}</p>
    <p>Grand Total: ${{ cartTotal(items) * 1.08 }}</p>
    <button @click="showDate = new Date()">Update Time</button>
  </div>
</template>

パフォーマンスがひどいです:

パフォーマンス監視ツールには、次のように表示されています:

「cartTotal関数は、ページ読み込みごとに5,000回呼び出される。100アイテム × 5,000 = 500,000回のアイテムごとの計算。3秒間のカクつき。」

(2) Vueのcomputedによる解決策:キャッシュ + 依存関係の追跡

JS
import { ref, computed } from 'vue'

const items = ref([
  { price: 100, quantity: 2 },
  { price: 50, quantity: 1 }
])

// ✅ computed:Rely solely on items,items If it hasn't changed, don't recalculate it.
const cartTotal = computed(() => {
  console.log('Calculating...')  // Only at items Print when changes occur
  return items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
})

// computed Auto-Processing 3 For use in: Count only 1 time
const tax = computed(() => cartTotal.value * 0.08)
const grandTotal = computed(() => cartTotal.value * 1.08)

(3) 収益

「computed」に切り替えた後:


3. computed() の基本的な使い方

(1) 標準構文

JS
import { ref, computed } from 'vue'

const count = ref(0)

// Basic Computational Properties
const doubleCount = computed(() => {
  return count.value * 2
})

// Abbreviation
const doubleCount = computed(() => count.value * 2)

// Usage
console.log(doubleCount.value)  // 0
count.value = 5
console.log(doubleCount.value)  // 10

(2) テンプレートでの使用

VUE
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ doubleCount }}</p>
    <button @click="count++">+1</button>
  </div>
</template>

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

const count = ref(0)
const doubleCount = computed(() => count.value * 2)
</script>

テンプレートでは、{{ doubleCount }} は必須ではありません .value(自動的に展開されます)。

(3) 5つの主な特徴

100%
graph TB
    subgraph ComputedKey Features
        A[Automatic Dependency Tracking]
        B[Cache Results]
        C[Responsive]
        D[Lazy Evaluation]
        E[Chainable calls]
    end
    
    style A fill:#42b883,color:#fff
    style B fill:#42b883,color:#fff
    style C fill:#42b883,color:#fff
    style D fill:#42b883,color:#fff
    style E fill:#42b883,color:#fff
機能 説明
依存関係の自動追跡 関数本体内で使用されているリアクティブデータを自動的に検出
キャッシュされた結果 依存関係に変更がない場合、複数回のアクセスは1回としてカウントされる
レスポンシブ 依存関係が変更された際に自動的に再計算される
遅延評価 アクセスされない限り評価されない(パフォーマンスの向上につながる)
連鎖可能 計算結果は他の計算済みプロパティに依存する場合がある

4. 計算式とメソッド:5つの主な違い

側面 計算値 方法
キャッシュ ✅ 依存関係に変更がない場合は再計算しない ❌ 呼び出しのたびに再計算する
レスポンシブ ✅ 依存関係を自動的に追跡 ❌ 追跡しない(手動でパラメータを渡す必要がある)
テンプレートへのアクセス {{ doubleCount }} {{ doubleCount() }}
パラメータ ❌ 未対応 ✅ 任意のパラメータを受け付ける
ユースケース 単純な派生値 複雑なロジック、イベント処理

(4) 5つのシナリオから選択可能

シナリオ 計算結果の使用 メソッドの使用
表示されている数字を2倍にする
カートの合計金額を計算
ボタンのクリックを処理する
APIリクエストを送信
日付の書式設定(依存関係なし)
フィルタ一覧(データによる)
フォームの検証

5. ゲッター/セッター:双方向の計算プロパティ

(1) デフォルトでは、ゲッターのみが存在します

JS
const firstName = ref('Alice')
const lastName = ref('Smith')

// There is only one getter
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

console.log(fullName.value)  // Alice Smith
// fullName.value = 'Bob Lee'  // ❌ Warning:Calculated properties are read-only.

(2) ゲッターとセッターの完全な実装

JS
const firstName = ref('Alice')
const lastName = ref('Smith')

const fullName = computed({
  // Read:Put it together
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  // Write:Unpack
  set(newValue) {
    const parts = newValue.split(' ')
    firstName.value = parts[0]
    lastName.value = parts[1] || ''
  }
})

// ✅ Read and write access is now available
fullName.value = 'Bob Lee'
console.log(firstName.value)  // Bob
console.log(lastName.value)   // Lee

(3) テンプレートにおける双方向バインディング

VUE
<template>
  <div>
    <input v-model="fullName">
    <p>First: {{ firstName }}</p>
    <p>Last: {{ lastName }}</p>
  </div>
</template>

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

const firstName = ref('Alice')
const lastName = ref('Smith')

const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (val) => {
    const parts = val.split(' ')
    firstName.value = parts[0]
    lastName.value = parts[1] || ''
  }
})
</script>

6. 計算プロパティへの連鎖呼び出し

(1) ネストされた計算式

JS
const items = ref([
  { price: 100, quantity: 2 },
  { price: 50, quantity: 3 }
])

// First Floor:Total Original Price
const subtotal = computed(() =>
  items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)

// Second Floor: Tax (Based on subtotal)
const tax = computed(() => subtotal.value * 0.08)

// Third Floor: Discount (Based on subtotal)
const discount = computed(() => subtotal.value > 200 ? 20 : 0)

// Fourth Floor:Final Total Price
const total = computed(() => subtotal.value + tax.value - discount.value)

console.log(total.value)  // 350 * 1.08 - 20 = 358

(2) 連鎖型演算キャッシュ機構

100%
graph TB
    A[items change] --> B[subtotal Recalculate]
    B --> C[tax Recalculate]
    B --> D[discount Recalculate]
    C --> E[total Recalculate]
    D --> E
    
    style A fill:#42b883
    style B fill:#42b883
    style E fill:#42b883

チェーンに依存する計算値のみが再計算され、使用されていないものは再計算されません。


7. 完全な例:Eコマースのショッピングカートの合計金額

(1) ▶ サンプル:. 基本的な計算

VUE
<template>
  <div>
    <p>Subtotal: ${{ subtotal }}</p>
    <p>Tax (8%): ${{ tax }}</p>
    <p>Discount: -${{ discount }}</p>
    <p><strong>Total: ${{ total }}</strong></p>
  </div>
</template>

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

const items = ref([
  { name: 'iPhone', price: 999, quantity: 1 },
  { name: 'Case', price: 50, quantity: 2 }
])

const subtotal = computed(() =>
  items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)

const tax = computed(() => subtotal.value * 0.08)
const discount = computed(() => subtotal.value > 1000 ? 50 : 0)
const total = computed(() => subtotal.value + tax.value - discount.value)
</script>
▶ 試してみよう

(2) ▶ サンプル:. ゲッターとセッター

VUE
<template>
  <div>
    <input v-model="fullName" placeholder="First Last">
    <p>First: {{ firstName }}</p>
    <p>Last: {{ lastName }}</p>
  </div>
</template>

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

const firstName = ref('Alice')
const lastName = ref('Smith')

const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (val) => {
    const parts = val.split(' ')
    firstName.value = parts[0] || ''
    lastName.value = parts[1] || ''
  }
})
</script>
▶ 試してみよう

(3) ▶ サンプル:. 計算されたチェーンコール

JS
import { ref, computed } from 'vue'

const score = ref(85)

// First Floor:Base Score
const baseScore = computed(() => score.value)

// Second Floor:Bonus Points(Base Score × Difficulty Level)
const difficultyBonus = computed(() => {
  if (baseScore.value >= 90) return 10
  if (baseScore.value >= 80) return 5
  return 0
})

// Third Floor:Level
const grade = computed(() => {
  const total = baseScore.value + difficultyBonus.value
  if (total >= 95) return 'A+'
  if (total >= 90) return 'A'
  if (total >= 80) return 'B'
  return 'C'
})
▶ 試してみよう

(4) ▶ サンプル:. 計算式とメソッドの比較

JS
// ❌ methods:Recalculate on every call
function getTotal() {
  console.log('Calculated!')  // If used multiple times in the template, it will be printed multiple times.
  return items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)
}

// ✅ computed: Count only 1 time
const total = computed(() => {
  console.log('Calculated!')  // Print Only 1 time
  return items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)
})
▶ 試してみよう

(5) ▶ サンプル:. 5つの性能比較シナリオ

シーン 呼び出されたメソッド数 計算された呼び出し数
テンプレート内で1回使用 1 1
テンプレート内で3回使用 3 1
テンプレート内で10回使用 10 1
親コンポーネントが更新されたが、項目は変更されていない 呼び出し済み 再計算されていない
依存関係の変更 再呼び出し 再計算

(6) ▶ サンプル:. よくある5つの間違い

エラー 症状 解決策
メソッドを計算式に置き換える 繰り返し使用するとパフォーマンスが低下する 計算式に切り替える
computed へのパラメータの指定が不正です エラー computed はパラメータに対応していません。代わりに methods を使用してください
セッター:独自に作成 無限ループ セッター:依存関係を修正してください。fullName.value = ... は使用しないでください
循環依存関係 A→B→A 無限ループ 依存関係のいずれかを削除する
計算プロパティ内に記述された複雑なロジック デバッグが困難 メソッドに抽出する。計算プロパティからそのメソッドを呼び出すようにする

❓ よくある質問

Q computedmethods、どちらを使うべきですか?
A 可能な限り computed を使用してください(キャッシュ機能+自動追跡機能)。methods を使用するのは、以下の状況に限ってください:(1) パラメータを渡す必要がある場合;(2) リアクティブデータに依存しない場合;(3) イベントハンドラ(@click など)の場合。
Q computed はパラメータを受け取ることができますか?
A いいえ。computed が受け取れるパラメータは 0 個のみです。パラメータを渡す必要がある場合は、代わりに methods を使用してください:getDouble(n) { return n * 2 }、あるいは computed で関数を返すようにしてください:computed(() => (n) => n * 2)
Q computedsetup の外部からアクセスできますか?
A はい。computedref オブジェクトを返すため、これを他のコンポーネントや関数に渡すことができます。ただし、リアクティブ性を維持するためには、computed を「派生リファレンス」として使用することをお勧めします。
Q computedwatch の違いは何ですか?
A computed は「派生値」(データに基づいて新しい値を計算するもの)であり、watch は「ウォッチャー」(データが変更された際に副作用を実行するもの)です。computedは純粋な計算に適しており、watchはリクエストの発行や状態の設定などに適しています。
Q computedmethodsと比べてどのような点で優れているのですか?
A キャッシュ機能です。methodsは呼び出しのたびに関数を実行しますが、computedは依存関係が変更された場合にのみ実行されます。テンプレート内でcomputedを3回使用しても計算は1回のみ行われますが、methodsを使用すると3回の計算が行われます。
Q 無限ループに陥らないようにセッターを記述するにはどうすればよいですか?
A セッター内では、計算式(computed)の依存関係(ref)のみを更新し、計算式自体に直接値を代入しないでください。たとえば、fullName のセッターでは、fullName.value = ... ではなく、firstNamelastName を更新する必要があります。
Q computed は非同期にできますか?
A いいえ。computed は同期型です。非同期操作を行う場合は、watch または watchEffect を使用してください。「非同期の計算プロパティ」が必要な場合は、data() { return { result: null } }watch + onMounted を組み合わせて使用してください。

📖 まとめ


📝 練習問題

(1) 基礎問題(難易度:⭐)

テキスト内の単語数を数える計算プロパティ wordCount を作成してください(テキストをスペースで分割するには split を使用します)。

データ:const text = ref('Hello Vue 3 from Alice') 出力結果は 4 になるはずです。

(2) 上級問題(難易度:⭐⭐)

3つの計算プロパティを含む、ショッピングカートの合計金額の計算式を作成してください:

(3) チャレンジ問題(難易度:⭐⭐⭐)

完全な「ショッピングカートの合計金額システム」を実装する:

  1. 5つの商品(それぞれに商品名、価格、数量が記載されている)
  2. 小計/税(8%)/割引(200以上の購入で20割引)/合計—4つの計算項目
  3. fullName 計算プロパティにはゲッターとセッターを使用します。セッター内では、入力値に基づいて firstName および lastName を更新します。
  4. 5つの計算プロパティについて、依存関係の追跡をテストする(どのプロパティが、いずれかの製品を変更した後に再計算されるか)
  5. Mermaid を使用して、計算プロパティの依存関係グラフを描く
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%