※ 2021/12/10 19:00 一部内容を訂正しました
はじめに
前回に続き、今回もVue.jsネタを。
配列などの値を<template>内で表示する場合、v-forなどのループを用いることは多いと思います。
そしてその際、配列のキーとなる値(≒key)が重複している値があると「duplicated key(=キーの重複)」警告が出ます。(エラーではない)
ただしこれがなぜか「扱う値に重複がないはずなのに出る」ケースがあります。
今回はそれについての原因と対応策です。
値が重複している
まず大前提として、配列内のkey値が重複している場合、それを<template>内でv-forループさせると「duplicated key」警告が出ます。
<template> <div id="app"> <input type="button" value="sample" @click="sampleFunc"> <ul> <!-- sampleFuncが実行された際、id=3が重複するので、duplicated keyが発生する --> <li v-for="item in items" :key="item.id">{{ item.value }}</li> </ul> </div> </template> <script> export default { data: function () { return { items: [ { id: 1, value: 'hoge' }, { id: 2, value: 'fuga' }, { id: 3, value: 'piyo' }, ], } }, methods: { sampleFunc: function() { // 重複した値を挿入する this.items.push( { id: 3, value: 'piyo' } ); }, } } </script>
これに関しては言うまでもなく「keyの値を重複させない」ようにすればよいです。
※よくあるのは「配列の初期化し忘れ」です。(といっても、意外と忘れがち...)
// こんな感じで、再設定前に一度初期化してあげる
items.splice(0, items2.length);
本題
で、次が本題。
下記のようなソースがあったとします。
<template> <div id="app"> <input type="button" value="sample" @click="sampleFunc"> <!-- itemsとitems2は配列もspanタグも別物...のはずなのに、これもduplicated keyが発生する --> <span v-for="item in items" :key="item.id">{{ item.value }}</span> <span v-for="item2 in items2" :key="item2.id">{{ item2.value }}</span> </div> </template> <script> export default { data: function () { return { items: [ { id: 1, value: 'hoge' }, { id: 2, value: 'fuga' }, { id: 3, value: 'piyo' }, ], items2: [ { id: 3, value: 'foo' }, { id: 4, value: 'bar' }, ], } } } </script>
itemsとitems2は配列もspanタグも別物...のはずなのに、<template>内のコメントで書いたように、これもduplicated keyが発生してしまいます。
なぜかというと、Vue.jsのv-forループのkeyは、「ある要素の子要素内全体で重複があってはならない」からです。
つまり上記ソースの例では、<div id="app">の子要素である<span>2つ(と<input>)のすべてで、同じ値のkeyがあってはならないのです。(itemsとitems2両方にid=3があるため、duplicated keyが発生する)
ここは盲点&面倒くさい点だと思います。
対策方法
対策方法ですが、とりあえず以下の2つくらいでしょうか?
keyにindexを使う- かぶらない値を設定する
keyにindexを使う
しかしindexは配列要素の追加や削除で変わるので(=indexに対応する値が変わる)、あまりお勧めできません。
v-forのindex(=配列のindex)を使えば、とりあえずduplicated keyは回避できます。
【2021/12/10 19:00追記】
indexを使うと必ず0始まりになるので、むしろduplicated keyが頻発してしまいますね。失礼しました。
なおduplicated keyが起こらないケースでも「indexは配列要素の追加や削除で変わるので(=indexに対応する値が変わる)、あまりお勧めできない」という事は変わりません。
【参考ページ】
かぶらない値を設定する
「かぶらない値を設定する」ですが、まず実際にDBなどに保存している値レベルで対応するのは困難だと思います。(0, 1, 2...など、各テーブル単位で数値などを設定するのが多いと思うので)
そうなると...なんかイケてない感がありますが、こんな感じにkey名を無理やり一意にする...しかないのかなあ、という感じです。
<template> <div id="app"> <input type="button" value="sample" @click="sampleFunc"> <!-- JavaScriptは同じなので省略 --> <!-- keyを無理やり絶対に重複しない値にする --> <span v-for="item in items" :key="`items-${item.id}`">{{ item.value }}</span> <span v-for="item2 in items2" :key="`items2-${item2.id}`">{{ item2.value }}</span> </div> </template>
なんかいい方法ないかなあ、という感じです。(Vue.js3.xではどうなんだろう)
短めですが、今回はここまで。