Vue 中应如何合理使用key值

在开发中经常会碰到使用 v-for的用法,例如现有数组
var array=[{name:’张三’,age:21},{name:’李四’,age:39},{name:’王五’,age:40}];
使用列表对此数据渲染如下

<ul>
<li v-for="person in array"
:key="Math.random()"
>
名称:<input v-model='person.name' /> 
</li>
</ul>

代码这么写,咋一看没什么问题,但是实际用的时候会发现每次输入一次后数据就会发生改变,然后输入框就会失去焦点。于是采取 v-model.lazy的方式,代码如下

<ul>
<li v-for="person in array"
:key="Math.random()"
>
名称:<input v-model.lazy='person.name' /> 
</li>
</ul>

这样数据不会事实更新,不会出现一输入就失去焦点的现象。但是引发一个问题,使用tab切换时无法定位到下一个输入框。于是这样改写:

<ul>
<li v-for="(person,index) in array"
:key="Math.random()"
>
名称:<input ref="'li-person-'+index" id="'li-person-'+index" v-model.lazy='person.name' @focus='focusEvent' /> 
</li>
</ul>
<script>
export default{
methods:{
    focusEvent(event){
        var target = event.srcElement;
           this.$nextTick(()=>{
             let id = target.getAttribute("id");
              if(id){
                let ta = this.$refs[id][0];
                if(ta)
                  ta.focus();
              }
         })
    }
}
}
</scirpt>

这样能解决数据改变时切换tab无法获取正确焦点的问题,但是使用element组件el-select依然无法获取焦点。因此对齐blur事件监听,处理与 focus类似,也是通过id找到对应的element,使其获取焦点。此处有一个小坑,就是不能直接通过target获取 元素,因为数据变动时这个element已经不存在了,故对其使用focus()会报错,因此必须在nextTick中获取该节点。但是,此处只能解决焦点问题,真正数据刷新后dom重新渲染失去焦点的解决方案还是没找到。
经过一系列采坑及查找资料

key
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的且唯一的 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值 (在这里使用简写):

<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>
建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

此处是摘取官网上的一段解释,通俗点来讲,就是一个列表项要有一个key值,这个key值如果唯一且未发生变化,则dom就会被复用,反之则需要重新生成。这段话解释了为什么我每次改变数据都会失去焦点的根本原因,因为我的key值是随机的,虽然是唯一的,但是在数据变动时就会被认为是另外一个元素,会被重新渲染,输入框也会被先移除再被添加上,因此,代码改为如下

<ul>
<li v-for="(person,index) in array"
:key="'person'+index"
>
名称:<input ref="'li-person-'+index" id="'li-person-'+index" v-model.lazy='person.name' @focus='focusEvent' /> 
</li>
</ul>

问题解决。
在实际中,我使用的远比这个例子复杂,是套用了两层v-for,但是实际原理是一致的。故做此记录

Logo

前往低代码交流专区

更多推荐