>웹 프론트엔드 >View.js >v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?

v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?

青灯夜游
青灯夜游앞으로
2022-01-22 15:05:004008검색

vue에서 v-for를 사용할 때 왜 index를 키로 사용할 수 없나요? 다음 글에서는 v-for의 핵심값이 index가 될 수 없는 이유를 소개하겠습니다. 도움이 되셨으면 좋겠습니다!

v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?

왜 v-for의 키값은 인덱스가 될 수 없나요?

많은 사람들이 이런 흔한 면접 질문을 하면 바로 가상 DOMdiff 알고리즘에 대해 이야기하기 시작합니다. 虚拟DOMdiff算法 了。

讲这些没问题,但如果是我,一定先讲 v-for 的 key 值写成 index 会造成的问题,再讲原理。

曾经我写 v-for, key 值永远都是 index,直到有一天,我这么写造成了线上bug...

来看一下我的线上bug演示吧:

父组件代码
<Child
  v-for="(item, index) in list"
  :key="index"
  :count="item.count"
  :name="item.name"
  @delete="handleDelete(index)"
/>

list: [
    {
      count: 1,
      name: &#39;第1个元素&#39;
    },
    {
      count: 2,
      name: &#39;第2个元素&#39;
    },
    {
      count: 3,
      name: &#39;第3个元素&#39;
    }
  ]
  
handleDelete(index) {
  this.list.splice(index, 1)
},

v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?

如代码和gif演示,点击删除第2个元素,看上去似乎一切正常。

等一下,第三个元素的count值居然变成了2,wtf!!!

惊得我又去看了遍子组件的代码

子组件
<div>
  <span>{{ name }}</span>
  count值为:{{ innerCount }}
  <button @click="$emit(&#39;delete&#39;)">-</button>
</div>

props: {
  count: {
    type: Number,
    default: 0
  },
  name: {
    type: String,
    default: &#39;&#39;
  }
},
data() {
  return {
    innerCount: this.count
  } 
}

感觉也没什么不对的啊。

不信邪,我又多创建了点元素来删除,还试了下排序:

v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?

果然,不光删除元素有问题,排序也有问题。

把 key 值改成 item.name 再试一下。

<Child
  v-for="(item, index) in list"
  :key="item.name"
  :count="item.count"
  :name="item.name"
  @delete="handleDelete(index)"
/>

v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?

正常了。

这样看来,在 v-for 里把 key 值写成 index,非常危险啊。

在查阅了 vue 官方文档之后,我终于明白了原因:

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。

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

不依赖子组件状态

子组件里有一行很关键的代码

data() {
  return {
    innerCount: this.count
  } 
}

子组件内部定义了 innerCount,这样子组件就有了自己的状态,按照官方文档的说明,这种情况下不能把 index 作为 key 值。

临时 DOM 状态

<div v-for="(item, index) in list1" :key="index">
  <input type="text" />
  <button @click="delClick(index)">删除</button>
</div>

v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?

删除了第2项,但是第3项在表单中的3变成了2,跟上面依赖子组件状态的例子是一样的。

总结

写列表渲染时, 依赖子组件状态或临时 DOM 状态的情况,如果有 删除、增加、排序这样的功能,不要把 index 作为 key。

事实上,写列表渲染时,永远不要把 index 做为 key,key 一定要是唯一标识。

至于原因,就要理解 diff

이런 얘기를 해도 괜찮지만 저라면 v-for의 핵심값을 인덱스로 써서 생기는 문제점을 먼저 이야기한 뒤 원리에 대해 이야기하겠습니다.

저는 한때 v-for를 썼는데 핵심 값은 항상 index였습니다. 어느 날 이 글을 쓰고
    온라인 버그
  • 가 발생했습니다...
  • 제 온라인 버그 데모를 살펴보겠습니다:
  • rrreee

예: 코드 및 gif 데모, 두 번째 요소를 삭제하려면 클릭하세요. 모든 것이 정상인 것 같습니다.

잠깐만요, 세 번째 요소의 개수 값이 실제로 2가 되었네요. 뭐! ! !

너무 놀라서 다시 서브컴포넌트의 코드를 살펴보니rrreee

아무 문제가 없다는 걸 느꼈습니다. 🎜🎜나는 악을 믿지 않기 때문에 삭제할 요소를 더 만들고 정렬을 시도했습니다: 🎜🎜v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?🎜🎜물론 요소 삭제 문제 뿐만 아니라 정렬 문제도 있습니다. 🎜🎜키 값을 item.name으로 변경하고 다시 시도하세요. 🎜rrreee🎜v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?🎜🎜 정상입니다. 🎜🎜v-for에서 키값을 인덱스로 쓰는 것은 매우 위험한 것 같습니다. 🎜🎜공식 Vue 문서를 참조한 후 마침내 이유를 이해했습니다. 🎜
🎜Vue가 v-for를 사용하여 렌더링된 요소 목록을 업데이트할 때 기본적으로 "내부 업데이트"를 사용합니다. 전략. 데이터 항목의 순서가 변경되면 Vue는 데이터 항목의 순서와 일치하도록 DOM 요소를 이동하지 않지만 각 요소를 제자리에 업데이트하고 각 인덱스 위치에서 올바르게 렌더링되도록 합니다. 🎜
🎜이 기본 모드는 효율적이지만 🎜하위 구성 요소 상태나 임시 DOM 상태(예: 양식 입력 값)에 의존하지 않는 목록 렌더링 출력🎜에만 적합합니다. 🎜
🎜🎜하위 구성 요소의 상태에 의존하지 않습니다🎜🎜🎜하위 구성 요소에 매우 중요한 코드 줄이 있습니다🎜rrreee🎜innerCount는 하위 구성 요소에서 내부적으로 정의되므로 하위 구성 요소는 자체 상태를 갖습니다. 공식 문서에 따르면 이 경우 index를 키 값으로 사용할 수 없습니다. 🎜🎜🎜임시 DOM 상태🎜🎜rrreee🎜4 .gif🎜🎜두 번째 항목은 삭제되지만 양식의 세 번째 항목은 2가 됩니다. 이는 위의 하위 구성 요소 상태에 의존하는 예와 같습니다. 🎜

요약🎜🎜목록 렌더링 시 하위 구성 요소의 상태나 임시 DOM 상태에 따라 다릅니다. 삭제, 추가, 정렬 등의 기능이 있는 경우에는 사용하지 마세요. 인덱스를 키로 사용합니다. 🎜🎜실제로 렌더링용 목록을 작성할 때 인덱스를 키로 사용하지 마십시오. 키는 고유 식별자여야 합니다. 🎜🎜이유를 이해하려면 diff 알고리즘을 이해해야 합니다. 🎜🎜답변할 질문: 🎜🎜🎜왜 키를 임의의 숫자나 타임스탬프로 쓸 수 없나요? 🎜🎜Key 왜 고유 식별자여야 합니까? 🎜🎜🎜걱정하지 마세요. Vue 이슈와 관련된 글 100개를 작성하도록 플래그를 설정해 두었으니 다음 글에서 천천히 분석해 보겠습니다. 🎜🎜제 Vue 시리즈 기사가 프런트엔드 분야에서 여러분에게 도움이 되기를 바랍니다~🎜🎜[관련 추천: 🎜vue.js 비디오 튜토리얼🎜]🎜

위 내용은 v-for 지시문의 키 값이 vue의 색인이 될 수 없는 이유는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제