Vueでv-forが動的に更新されない?再描画されない時の対応テクニックまとめ

Vue.jsでリストレンダリングをする際、v-forを使って配列やオブジェクトを展開するのが一般的です。しかし、動的な更新を加えてもなぜか表示が更新されない、期待通りに反映されないという経験をしたことがある方も多いのではないでしょうか。

本記事では、その原因と対処法を包括的に整理します。特にkeyの設定やVueのリアクティブシステムの制限、配列操作の落とし穴などに焦点を当て、安定したリスト再描画のテクニックを紹介します。

1. v-forの再描画がされない原因の基本理解

Vueのv-forは、リストレンダリングのための構文ですが、ただ値を変更しただけではDOMが再描画されない場合があります。その原因として、Vueのリアクティブシステムが「変更を検出できない操作」に対して無反応であることが挙げられます。

Vue 2系では特に、以下のような操作に注意が必要です。

  • 配列のインデックスを直接代入する(例:this.items[1] = newItem
  • オブジェクトに新しいプロパティを追加する(例:this.obj.newKey = value
  • spliceではなくpushpopでリストの長さを変えないと再描画が走らないケース

Vueはこれらの操作に対して再レンダリングのトリガーが発生しない場合があるため、手動でのリアクティブ更新が求められます。

2. keyの付け方を誤ると再描画されない

Vueのドキュメントでも強調されていますが、v-forで展開する要素には必ず一意なkeyを付ける必要があります。これはVueの仮想DOMの差分アルゴリズムが正確な変更を検出するために必須です。

<li v-for="item in items" :key="item.id">{{ item.name }}</li>

このkeyが、

  • 同じ値を繰り返している
  • 配列のインデックス(index)などで済ませている

といった場合、Vueが変更を検知できず、再描画がスキップされることがあります。

適切なkeyの選び方

  • idなどのユニークな識別子を使う
  • 配列が動的に変更される場合は、インデックスよりもデータ自体の一意性を優先する
  • ソートやフィルタの後にindexを使うと意図しない要素が再利用されることがある

このkeyの設計は、v-forの安定性に直結する重要ポイントです。

3. Vue 2でのリアクティブ制限とその回避方法

Vue 2には、JavaScriptの制限と内部実装の都合から、リアクティブに扱えない操作が存在します。これが原因でv-forに変更が反映されないことがあります。

リアクティブにならない代表例

  • 配列要素のインデックス指定での代入
  • Object.assignや直接のプロパティ追加
  • deleteArray.lengthの強制変更
// NG: これでは再描画されない
this.items[1] = { name: 'new' }

// OK: Vue.setを使う
this.$set(this.items, 1, { name: 'new' })

Vue 3ではこれらの問題はProxyベースで大きく改善されていますが、Vue 2を使用している場合は、必ずVue.setthis.$setなどを活用しましょう。

回避テクニック

  • 配列の要素を更新するときはspliceを使う
  • オブジェクトのプロパティ追加にはVue.set(obj, key, value)を使う
  • Object.freezeされたデータは再描画されないので注意

4. spliceと配列破壊的操作のベストプラクティス

Vueで配列を再描画させたいときには、spliceやスプレッド演算子などの明示的な変更操作が有効です。Vueは破壊的な変更よりも、新しい参照の生成を伴う非破壊的変更に強く反応します。

// 再描画されやすい例
this.items = [...this.items, { id: 4, name: '新しいアイテム' }]

また、既存配列の中身を変更する場合でも、spliceがもっとも確実な手段です。

// 2番目の要素を差し替える
this.items.splice(1, 1, { id: 2, name: '変更後' })

pushやpopでうまくいかない場合の対策

  • Vueが再描画を最適化する結果、push/popだけでは描画に反映されない場合がある
  • その場合は、配列全体を再代入する(this.items = [...this.items]
  • 配列のフィルタ処理後などは必ず新しい配列を返すようにする

5. フォースアップデートや一時的keyリセットという裏技

どうしても再描画が発生しない、Vueの再評価がうまくいかない場合には、強制的にDOMを再レンダリングさせる方法も存在します。

フォースアップデート

this.$forceUpdate()

ただし、これはVueの設計思想から外れるため、あくまでデバッグや応急処置として使うべきです。

keyリセットによる再レンダリング

v-forに使っているkeyに意図的に変更を加えることで再レンダリングを発生させることも可能です。

<template>
  <component-list :items="items" :key="renderKey" />
</template>

<script>
export default {
  data() {
    return {
      items: [],
      renderKey: 0
    }
  },
  methods: {
    refreshList() {
      this.renderKey += 1
    }
  }
}
</script>

この方法はパフォーマンスに注意が必要ですが、再描画が発生しない特殊ケースには有効です。

6. まとめ

Vueでv-forが再描画されない場合は、DOMや配列の変更がVueのリアクティブシステムに正しく認識されていない可能性が高いです。

以下のポイントを押さえることで、再描画の問題を回避できます。

  • v-forには必ずユニークなkeyを付ける
  • 配列やオブジェクトの更新にはVue.setspliceを使用
  • インデックスでの直接代入は避ける
  • フォースアップデートは最後の手段として使う
  • 配列を非破壊的に更新し、再代入を活用する

Vue 3以降ではリアクティブの仕組みが大幅に改善されていますが、Vue 2を使用しているプロジェクトでは、これらの制約を理解して設計することが求められます。

参考

タイトルとURLをコピーしました