JavaScript
vue.js

Vue.js: v-forで項目インデックスをkey属性にしていいのか

v-forディレクティブを使うと、データに納められた複数の項目がページに要素として差し込めます。このときv-forkeyを与えるのが、「スタイルガイド」に定められた「必須」のルールです(「キー付き v-for」)。


一意のkey特別属性とインデックス

key特別属性は、要素に一意の値を与えます。:keyv-bind:keyの省略構文です(「key」参照)。

<ul>

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

また、配列からv-forでデータを取り出すとき、第2引数に配列インデックスが受け取れます(「v-for で配列に要素をマッピングする」参照)。インデックスはもちろん一意です。

<ul id="example">

<li v-for="(item, index) in items">
{{parentMessage}} - {{index}} - {{item.message}}
</li>
</ul>

では、v-forで配列インデックスを得て、keyに与えればよいのではないでしょうか。結論からいいますと、配列インデックスはkeyの値とすべきではありません。


keyを与えない場合は、配列インデックスが使われるとのことです。

また、要素の追加や削除あるいは順序変更がない場合にかぎれば、keyを使わなくても問題はないようです。むしろ「標準のモードは効率がいい」とされます。ただ、あとで変わりやすい前提と思われるので、keyを「必須」にしているのではないかと推測されます。



インデックスをキーにすると要素のアニメーションが意図しない動きになる

v-forで得た配列インデックスをkeyの値に与えると、要素のアニメーションが意図しない動きになったりします。「Vue.js: 動的に変更する要素にアニメーションを加える」でつくったTodoリストは、項目を加えたり除いたりすると水平アニメーションします(サンプル001)。


サンプル001■Vue.js + ES6: TodoMVC with animation

vuejs_todomvc_5.png

>> jsdo.itへ

<li v-for="todo in filteredTodos"

:key="todo.id"
>

</li>

データをはじめに読み込んだとき(todoStorage.fetch())は、配列インデックスを項目のkeyに与え、配列の長さを新たに加える項目の値(todoStorage.uid)としています。そのうえで、項目を加える(addTodo())たびに値を加算するのです。keyの値は一意であるとともに、ひとたびつけられた項目の値は決して変わりません。

const todoStorage = {

fetch() {

todos.forEach((todo, index) =>
todo.id = index
);
todoStorage.uid = todos.length;
return todos;
},

};

const app = new Vue({

methods: {
addTodo() {

this.todos.push({
id: todoStorage.uid++,
title: value,

});

},

},

});

このテンプレートをつぎのように書き替え、配列インデックスがkeyの値となるようにします(サンプル002)。

<li v-for="(todo, index) in filteredTodos"

:key="index"
>

</li>


サンプル002■Vue.js + ES6: Setting the index of item to its :key

vuejs_todomvc_5.png

>> jsdo.itへ

すると、なんということでしょう。どの項目を削除してもアニメーションするのは最後の項目になってしまいます。


図001■どの項目を削除しても最後の項目がアニメーションする

1807001_001.png

最初の項目を削除

1807001_002.png

アニメーションするのは最後の項目


keyは振り直してはいけない

一意のkeyは、どのデータが変わったのか、Vueが追いかけるために用いられます。ところが、配列インデックスを使ってしまうと、前の要素が除かれたら後の番号はつけ変えなければなりません。すると、Vueは変わったデータを見失ってしまいます。つまり、keyは振り直してはいけないのです。

それでも、データの削除そのものは正しく行われます。けれども、アニメーションが求められると、要素をすぐに消すわけにはいきません。Vueは改めてデータの最後につけ加えるようです(「キー付き v-for」の「詳細な説明」参照)。そして、要素のもとの配置にもとづいてアニメーションさせます。そのとき、インデックスでkeyを振り直すと、最後の要素と取り違えてしまうというのが、前掲サンプル002の動きと考えればよいでしょう(図001)。

前掲サンプル001は、もとデータの配列の長さをつぎのkeyの値とし、項目が加わるたびに加算しました。一意であるとともに、変わらぬ値が振られるので確実です。もっとも、keyの値を画面に表示したとき、追加・削除を繰り返すと、番号の開きが大きくなるのは気になるかもしれません。その場合は「追加と削除が繰り返される配列要素のオブジェクトに一意のid番号を振る」をお読みください。