自学内容网 自学内容网

Diff 算法的误判

起源:

设想一下,假如你桌面上的文件都没有文件名,取而代之的是,你使用通过文件的位置顺序即index来区分它们———第一个文件,第二个文件,以此类推。也许这种方式可行,可是一旦你删除了其中的一个文件,这种组织方式就会变得混乱无比。原来的第二个文件可能会变成第一个文件,第三个文件会成为第二个文件……引出我们的主角问题:for循环的:key的值使用index绑定,当循环列表条目变化更新,导致虚拟 DOM Diff 算法认为原有项被替换,而更新

// vue2写法 错误例子
<template>
  <div>
    <button @click="addItem">添加项目</button>
    <button @click="removeItem">删除项目</button>
    <ul>
      <li v-for="(item, index) in list" :key="index">
        {{ item.name }} (index: {{ index }})
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        { name: 'Item 1' },
        { name: 'Item 2' },
        { name: 'Item 3' }
      ]
    };
  },
  methods: {
    addItem() {
      this.list.splice(1, 0, { name: `New Item` }); // 在索引1的位置插入一个新项
    },
    removeItem() {
      this.list.splice(0, 1); // 删除索引0位置的项
    }
  }
};
</script>
// vue3 错误例子
<template>
  <div>
    <button @click="addItem">添加项目</button>
    <button @click="removeItem">删除项目</button>
    <ul>
      <li v-for="(item, index) in list" :key="index">
        {{ item.name }} (index: {{ index }})
      </li>
    </ul>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    // 定义列表为响应式数据
    const list = ref([
      { name: 'Item 1' },
      { name: 'Item 2' },
      { name: 'Item 3' }
    ]);

    // 添加项目方法
    const addItem = () => {
      list.value.splice(1, 0, { name: 'New Item' }); // 在索引1的位置插入一个新项
    };

    // 删除项目方法
    const removeItem = () => {
      list.value.splice(0, 1); // 删除索引0位置的项
    };

    return {
      list,
      addItem,
      removeItem
    };
  }
};
</script>

解决方法:

不要使用 index 作为 key,而是使用数据中的唯一标识符或者随机数,这样可以避免 Diff 算法的误判,确保列表在更新时能够正确地追踪每个项的状态,提升渲染性能。

//vue2写法
<template>
  <div>
    <button @click="addItem">添加项目</button>
    <button @click="removeItem">删除项目</button>
    <ul>
      <li v-for="item in list" :key="item.id">
        {{ item.name }} (id: {{ item.id }})
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        { id: generateRandomId(), name: 'Item 1' },
        { id: generateRandomId(), name: 'Item 2' },
        { id: generateRandomId(), name: 'Item 3' }
      ]
    };
  },
  methods: {
    addItem() {
      // 使用随机 id 添加新项
      this.list.push({ id: generateRandomId(), name: `New Item` });
    },
    removeItem() {
      this.list.splice(0, 1); // 删除索引0位置的项
    }
  }
};

// 生成随机 id 的函数
function generateRandomId() {
  return Math.floor(Math.random() * 1000000); // 生成一个 6 位的随机数
}
</script>
// vue3 写法,正确例子
<template>
  <div>
    <button @click="addItem">添加项目</button>
    <button @click="removeItem">删除项目</button>
    <ul>
      <li v-for="item in list" :key="item.id">
        {{ item.name }} (id: {{ item.id }})
      </li>
    </ul>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    // 定义列表为响应式数据
    const list = ref([
      { id: generateRandomId(), name: 'Item 1' },
      { id: generateRandomId(), name: 'Item 2' },
      { id: generateRandomId(), name: 'Item 3' }
    ]);

    // 生成随机 id 的函数
    function generateRandomId() {
      return Math.floor(Math.random() * 1000000); // 生成一个 6 位的随机数
    }

    // 添加项目方法
    const addItem = () => {
      list.value.push({ id: generateRandomId(), name: 'New Item' });
    };

    // 删除项目方法
    const removeItem = () => {
      list.value.splice(0, 1); // 删除索引0位置的项
    };

    return {
      list,
      addItem,
      removeItem
    };
  }
};
</script>

Diff算法学习:

概念:

Diff算法主要通过比较两个数据结构,并找出最小的插入、删除或修改操作,将一个数据结构变成另一个。例如,在比较两个字符串或数组时,算法会找出最小的变化(即最少的增删改操作),使一个序列变成另一个。

原理:

Diff 算法的主要思想是找到两个序列之间的最长公共子序列,从而计算出需要的修改步骤。通常通过动态规划来实现 LCS,从而计算出最小的修改路径

Diff 算法步骤通常如下:

  1. 比较两个数据结构的每一个元素。
  2. 如果两个元素相同,则无需操作,直接继续。
  3. 如果两个元素不同,则记录变化的操作(例如插入、删除或替换)。
  4. 重复以上步骤,直到找到最优路径。

原文地址:https://blog.csdn.net/m0_72030584/article/details/143791796

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!