九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
今開発をしているサイトでは以前よりも JavaScript での動きが多いということで私の大好きな(=学習コストが低い) Vue.js をフル活用しています。
Vue は使うたびにその便利さにびっくりさせられているのですが、今日ひとつ v-for について気をつけておかなければいけないことに遭遇したんで未来の自分のためにもこの記事を書きたいと思います。
[こんな時]
まずはどんなときに影響あるの??を私が遭遇した実際の例を見てみましょう。
1.アポイントメントの一覧を表示
2.表示されている各データを X-editable でポップアップ表示しテキストや日付をリアルタイム変更
3.更新が成功したら新しくなったアポイントメントデータを読み込んで一覧表示 ←(ここで一覧表示がうまくいかなくなる
※ここで重要なのはデータ更新後のリフレッシュがHTTPじゃなく、JavaScriptを使ってる部分です。
[具体的にどんな不具合が起こった??]
これも簡単な例でみてみましょう。
次のはさっき説明したアポイントメント一覧の簡易バージョンです。
※もちろんこの部分は Vue.js の v-for=”**** in ****” をつかってクルクル回して表示をしてます。
※ソートは日付の降順ね。
【データ例】
アポイントメント1 | 2017/08/10
アポイントメント2 | 2017/08/05
アポイントメント3 | 2017/08/01
ここで、2番めの日付を「2017/08/15」に変更します。
アポイントメント1 | 2017/08/10
アポイントメント2 | 2017/08/05 ←ここを 2017/08/15 に変更しよう!
アポイントメント3 | 2017/08/01
【予想してたのは、、、??】
もちろん以下のような状態になると思っていました。
アポイントメント2 | 2017/08/15 ←トップに上がった★
アポイントメント1 | 2017/08/10
アポイントメント3 | 2017/08/01
【でも現実は、、、、??】
本当はもっと複雑ですが、わかりやすくするためにこれも簡略版で。
アポイントメント1 | 2017/08/15 ←え!?変更したのは「アポイントメント2」なんだが、、
アポイントメント2 | 2017/08/10
アポイントメント3 | 2017/08/01
※でもこの状態で、F5を押して再表示するとうまくいっている。
うーーーん、、、、、なんでだろうか、、、、最近ジジイの目は酷使されて限界なんだがー(汗)
そこでいつものごとくGoogleさんで検索(←最近良い結果を出さなくなりましたよねー。僕だけかな??検索下手??? DuckDuckGoなんかが対抗馬になったら面白いのにな(笑))してみると Vue の助け合いフォーラムを発見しました。
https://forum.vuejs.org/t/how-to-disable-component-reuse/5828
要約すると以下のような感じです。
相談者「コンポーネントの再利用をしない方法ってねーの??」
回答者「key アトリビュート使いなよ。ガイドに乗ってんじゃーん」
相談者「マジか!?魔法みたくうまくいったわ。あざーすあざーす!」
ん?? key attribute ??って Vue使い始めて初めて聞くけど何なんだろう。
本家ドキュメントへ急げ↓↓↓
https://jp.vuejs.org/v2/guide/list.html#key
ふむふむ。
どうやら Vueはデフォルトでさっきの相談者が聞いてたコンポーネントの再利用を実行するようになっているらしく、それで高速表示ができるようになっているみたい。
で、もしデータ変更で表示順番が変わってしまったら再利用の影響で表示がおかしくなっちゃうことがあるよ、ってこと(と解釈しました)。
[じゃ、どうすればいーの???]
ガイドにしっかりとのってました。
以下は引用です。
<div v-for="item in items" :key="item.id"> <!-- content --> </div>
ポイントは「:key=”item.id”」の部分。
そうです。keyというプロパティにユニークなIDを指定しておくことで、もしデータの順番が変わってしまっても問題なく表示できるようになるよっていう機能のなんですね。さすが Vue。こういうところにもきちんと気配りしてくれている!
ってことで、私の場合も魔法のように一発で解決。
・・・でもこれって今後も発生する可能性もあるから覚えとかなきゃねってことで今回の記事は終わりです。m_ _m
※ちなみに最近 nextTick() という便利な機能をしりました。DOMが変更になってからコールバック関数を実行してくれます。以下も引用。
// データを変更する vm.msg = 'Hello' // DOM はまだ更新されていません Vue.nextTick(function () { // DOM が更新されています })
Vue の奥はまだまだ深そうだ(^^)