Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么? #37

Open
lovelmh13 opened this issue Feb 16, 2020 · 0 comments

Comments

@lovelmh13
Copy link
Owner

lovelmh13 commented Feb 16, 2020

不用key有的情况看其实是没啥问题(无状态组件),但是我们来看一下什么时候会出问题。为什么会出问题。

不写key

上来先举个例子,来展示不写key的直观的影响:

<template>
  <div>
    <div>
      <input type="text" v-model="name">
      <button @click="add">添加</button>
    </div>
    <ul>
      <li v-for="(item, i) in list">
        <input type="checkbox"> {{item.name}}
      </li>
    </ul>
  </div>
</template>

<script>
 export default {
  data() {
    return {
      name: '',
      newId: 2,
        list: [
          { id: 1, name: '日向' },
          { id: 2, name: '影山' },
        ]
      }
    },
    methods: {
      add() {
        this.list.unshift({ id: ++this.newId, name: this.name })
        this.name = ''
      }
    }
}
</script>

此时,不写key,我们来看看新加一条数据(数据是从头部添加的),会出现什么情况。 在新加数据前,我们先选中一个选项。然后再添加。
1
2

添加完成以后,刚才选了第一个,日向。结果添加以后,选中的还是第一个,不过第一个已经变成了新添加进来的大地了。

key

当我们把key加上:

<template>
  <div>
    <div>
      <input type="text" v-model="name">
      <button @click="add">添加</button>
    </div>
    <ul>
      <li v-for="(item, i) in list" :key="item.id">
        <input type="checkbox"> {{item.name}}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: '',
      newId: 2,
      list: [
        { id: 1, name: '日向' },
        { id: 2, name: '影山' },
      ]
    }
  },
  methods: {
    add() {
      this.list.unshift({ id: ++this.newId, name: this.name })
      this.name = ''
    }
  }
}
</script>

3

我们写上了key,就算是重新插入,也不会出现选择错误的情况,依然选择的是日向。

当组件有状态的时候,就会出现问题。

官网的说明

key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

现在理解一下官网的这句话,不加key,会复用元素,所以才出现了一开始的错误。

不写key的时候:官网是这么说的:

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性

Diff算法

借用一下别人的Diff的文章,来解释一下:

vue和react的虚拟DOM的Diff算法大致相同,其核心是基于两个简单的假设
首先讲一下diff算法的处理方法,对操作前后的dom树同一层的节点进行对比,一层一层对比,如下图:

image

当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程默认情况下也是遵循以上原则。
比如一下这个情况:

image

我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:

image

即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

image

vue中列表循环需加:key="唯一标识" 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM

参考:

VUE中演示v-for为什么要加key
官网

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant