侧边栏壁纸
博主头像
woku博主等级

成功的路上并不拥挤

  • 累计撰写 50 篇文章
  • 累计创建 13 个标签
  • 累计收到 3 条评论
vue

vue的就地更新与v-for中key属性

woku
2022-03-15 / 0 评论 / 1 点赞 / 158 阅读 / 2,897 字

什么是“就地更新”

官方文档给出的解释:

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

vue在更新视图时候,如果发现DOM结构是一样的,那么会复用原来的DOM结构,只是更新DOM结构里面的内容,来做到性能上的提升

<template>
<div>
  <ul>
    <li v-for="(item, index) of list">
      <span>{{ item.value }}</span>
      <button @click="doDelete(index)">DELETE</button>
  </li>
  </ul>
  </div>
</template>

<script>
  export default {
    name: "test",
    data() {
      return {
        list: [{
          id: 1,
          value: 'item-1'
        }, {
          id: 2,
          value: 'item-2'
        }, {
          id: 3,
          value: 'item-3'
        }]
      }
    },
    methods: {
      doDelete(index) {
        this.list.splice(index, 1)
      }
    }
  }
</script>

image.png

就地更新策略做的事情

  • 当删除第二项的时候,list的数据发生了变化 [item-1, item-2, item-3] => [item1, item3]
  • item-2在数据层面是删除的第二项,但是在DOM结构上,不会被移除,而是复用第二个节点,此时数据的第二项已经是item-3了,于是就第二个DOM这个地更新内容为item-3.
  • 数据由长度为3变为长度为2,最后一个多余的DOM节点会被删除

发现点击第二个delete按钮时,第二个li节点的span里面的内容会闪一下,这就说明span里面的内容是有变化的,而不是直接的把第二个li节点移除。

就地更新在v-for中的问题

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

  • 如果li里面出现了input,由于DOM节点是复用的,那么input里面这个值的状态也是会保留的
<template>
    <div>
        <ul>
            <li v-for="(item, index) of list">
                <span>{{ item.value }}</span>
                <input type="text">
                <button @click="doDelete(index)">DELETE</button>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        name: "test",
        data() {
            return {
                list: [{
                    id: 1,
                    value: 'item-1'
                }, {
                    id: 2,
                    value: 'item-2'
                }, {
                    id: 3,
                    value: 'item-3'
                }]
            }
        },
        methods: {
            doDelete(index) {
                this.list.splice(index, 1)
            }
        }
    }
</script>
  • 子组件里面的状态也不会更新

子组件结构如下:

const myComponent = {
  props: {
    num: Number
  },
  template: `
  <span>{{ num }}</span>
  `
 }
<template>
<div>
  <ul>
    <li v-for="(item, index) of list">
      <span>{{ item.value }}</span>
      <my-component :num="index"></my-component>
      <input type="text">
      <button @click="doDelete(index)">DELETE</button>
  </li>
  </ul>
  </div>
</template>

<script>
  export default {
    components: {
      myComponent
    },
    data() {
      return {
        prefixCls: 'not-found',
        list: [{
          id: 1,
          value: 'item-1'
        }, {
          id: 2,
          value: 'item-2'
        }, {
          id: 3,
          value: 'item-3'
        }]
      }
    },
    methods: {
      doDelete(index) {
        this.list.splice(index, 1)
      }
    }
  }
</script>

key解决这个问题

key必须是唯一的

key确定好了就不能变化

<template>
    <div>
        <ul>
            <li v-for="(item, index) of list" :key="item.id">
                <span>{{ item.value }}</span>
                <input type="text" :value="tempArr[index]">
                <button @click="doDelete(index)">DELETE</button>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                list: [{
                    id: 1,
                    value: 'item-1'
                }, {
                    id: 2,
                    value: 'item-2'
                }, {
                    id: 3,
                    value: 'item-3'
                }],
                tempArr: [1, 2, 3]
            }
        },
        methods: {
            doDelete(index) {
                this.list.splice(index, 1)
            }
        }
    }
</script>

当删除第二项的时候,有key那么vue就会根据最新的数据对DOM进行调整,不采用就地更新策略。

删除第二项的时候,input里面的值还是2,而不是3

删除第二项的时候,原来第三项的index变成了1,此时tempArr下标为1的值还是2,.

所以在操作数据的时候,key尽量不要使用index, index会随着数据的变化也可能会变化。

1

评论区