如何为Vue的v-for生成唯一key,解决使用index作为key的动画异常问题
问题原因分析
你遇到的动画异常本质是v-for用index作为key的经典问题:
- 当你删除列表中间某一项时,它后面所有元素的索引都会整体前移1位,对应的
key也会同步变化 - Vue的diff算法会通过key判断元素是否是同一个实例,key变了就会认为是旧元素销毁、新元素创建,触发transition-group的过渡动画,这就是最后一个元素总会无故触发动画的根因
- 你的列表允许存在重复文本,所以直接用
msg当key也不可行。
Symbol是否可用?
不推荐用Symbol作为key:
- Vue2要求key的类型为字符串/数字原始值,Symbol在diff过程的对比逻辑中会存在隐性转换问题,可能引发不可预期的渲染异常
- Symbol无法被JSON序列化,后续如果需要做列表数据持久化、跨端传递都会遇到问题。
最优解决方案
把纯字符串的列表项改造为带唯一自增ID的对象即可,实现成本极低,且完全满足你的需求。
修改后的代码如下:
<!DOCTYPE html> <header> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> </header> <body> <div id="message"> <transition-group name="msgAnimation" tag="div"> <!-- key改用唯一id --> <div v-for="(msg, i) in messages" :key="msg.id" class="wrapper"> <div class="wrapper__block" @click="messages.splice(i, 1)"> {{ msg.content }} </div> </div> </transition-group> </div> </body> <style> #message { display: flex; flex-direction: column; } .msgAnimation-enter-active, .msgAnimation-leave-active { transition: opacity 1s; } .msgAnimation-enter, .msgAnimation-leave-to { opacity: 0; } .wrapper { width: 100%; height: 9vmin; margin: 1vmin; } .wrapper__block { background: green; height: 100%; width: 100%; } </style> <script> // 自增ID生成器,新增列表项时调用即可得到唯一ID let msgIdCounter = 0 new Vue({ el: '#message', data: { messages: [ "hi", "hi", "123321", "hi", "32112332112sdfs", "hi", "qweewq", "ashjdddsa", "asdfddsa", "asggghjddsa", "ashddsa", "asjghjddsa", "asjddsa", "asdddsa", // 初始化时给每一项加唯一id ].map(content => ({id: msgIdCounter++, content})) }, }) </script> </html>
如果后续需要新增列表项,直接用messages.push({id: msgIdCounter++, content: '新增的内容'})即可,保证所有key唯一且不会随着列表元素的增删发生变化,不会再触发多余的过渡动画。
内容的提问来源于stack exchange,提问作者Gorilka
相关产品推荐
相关产品推荐

