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
ではなくpush
やpop
でリストの長さを変えないと再描画が走らないケース
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
や直接のプロパティ追加delete
やArray.length
の強制変更
// NG: これでは再描画されない
this.items[1] = { name: 'new' }
// OK: Vue.setを使う
this.$set(this.items, 1, { name: 'new' })
Vue 3ではこれらの問題はProxy
ベースで大きく改善されていますが、Vue 2を使用している場合は、必ずVue.set
やthis.$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.set
やsplice
を使用 - インデックスでの直接代入は避ける
- フォースアップデートは最後の手段として使う
- 配列を非破壊的に更新し、再代入を活用する
Vue 3以降ではリアクティブの仕組みが大幅に改善されていますが、Vue 2を使用しているプロジェクトでは、これらの制約を理解して設計することが求められます。
参考
- Vue公式ガイド(List Rendering)https://jp.vuejs.org/v2/guide/list.html
- Vue公式API:Vue.set()https://jp.vuejs.org/v2/api/#Vue-set
- Qiita:Vueのv-forとkeyの重要性https://qiita.com/otsukayuhi/items/8e897fdf5d72d8d61741