IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 从源码看vue(v3.2.41)中的diff原理 -> 正文阅读

[JavaScript知识库]从源码看vue(v3.2.41)中的diff原理

vue3的diff和vue2有一点不同,vue2有前后、后前和遍历父节点查找比较过程。vue3就直接将这几个过程改成了使用最长递增子序列算法去求最小移动步骤。接下来我们重点看涉及递增子序列算法部分,前几个过程和vue2类似就不讲解。

dom节点:

原dom
<div key="A">1</div>
<div key="B">2</div>
<div key="C">3</div>
<div key="D">4</div>
<div key="E">5</div>
<div key="F">6</div>
<div key="g">7</div>
<div key="h">8</div>
更改后
<div key="A">1</div>
<div key="D">4</div>
<div key="C">3</div>
<div key="B">2</div>
<div key="E">5</div>
<div key="g">7</div>
<div key="F">6</div>
<div key="h">8</div>

值为1和8的会在前面几个步骤先处理,所以这里只会有中间的dom,我们来具体看看是怎么处理中间部分的:

const s1 = i; // prev starting index
const s2 = i; // next starting index
 // 5.1 build key:index map for newChildren
 const keyToNewIndexMap = new Map();
 for (i = s2; i <= e2; i++) {
     const nextChild = (c2[i] = optimized
         ? cloneIfMounted(c2[i])
         : normalizeVNode(c2[i]));
     if (nextChild.key != null) {
         if (keyToNewIndexMap.has(nextChild.key)) {
             warn$1(`Duplicate keys found during update:`, JSON.stringify(nextChild.key), `Make sure keys are unique.`);
         }
         keyToNewIndexMap.set(nextChild.key, i);
     }
 }
 // 5.2 loop through old children left to be patched and try to patch
 // matching nodes & remove nodes that are no longer present
 let j;
 let patched = 0;
 const toBePatched = e2 - s2 + 1;
 let moved = false;
 // used to track whether any node has moved
 let maxNewIndexSoFar = 0;
 // works as Map<newIndex, oldIndex>
 // Note that oldIndex is offset by +1
 // and oldIndex = 0 is a special value indicating the new node has
 // no corresponding old node.
 // used for determining longest stable subsequence
 const newIndexToOldIndexMap = new Array(toBePatched);
 for (i = 0; i < toBePatched; i++)
     newIndexToOldIndexMap[i] = 0;
 for (i = s1; i <= e1; i++) {
     const prevChild = c1[i];
     if (patched >= toBePatched) {
         // all new children have been patched so this can only be a removal
         unmount(prevChild, parentComponent, parentSuspense, true);
         continue;
     }
     let newIndex;
     if (prevChild.key != null) {
         newIndex = keyToNewIndexMap.get(prevChild.key);
     }
     else {
         // key-less node, try to locate a key-less node of the same type
         for (j = s2; j <= e2; j++) {
             if (newIndexToOldIndexMap[j - s2] === 0 &&
                 isSameVNodeType(prevChild, c2[j])) {
                 newIndex = j;
                 break;
             }
         }
     }
     if (newIndex === undefined) {
         unmount(prevChild, parentComponent, parentSuspense, true);
     }
     else {
         newIndexToOldIndexMap[newIndex - s2] = i + 1;
         if (newIndex >= maxNewIndexSoFar) {
             maxNewIndexSoFar = newIndex;
         }
         else {
             moved = true;
         }
         patch(prevChild, c2[newIndex], container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
         patched++;
     }
 }

首先遍历新节点并设置keyToNewIndexMap,这个是新节点对应的index值:
在这里插入图片描述
随后会去遍历老节点,并查找新节点在老节点中的索引值index,如果找到了就会设置在newIndexToOldIndexMap,如果没找到说明新的节点不需要该节点,则会删除该节点:
在这里插入图片描述
在这里插入图片描述
之所以要建立新老节点的索引列表是因为要求出最小递增子序列,通过该序列可以确定哪些节点可以不用动:

const increasingNewIndexSequence = moved
   ? getSequence(newIndexToOldIndexMap)
    : EMPTY_ARR;
j = increasingNewIndexSequence.length - 1;
// looping backwards so that we can use last patched node as anchor
for (i = toBePatched - 1; i >= 0; i--) {
    const nextIndex = s2 + i;
    const nextChild = c2[nextIndex];
    const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;
    if (newIndexToOldIndexMap[i] === 0) {
        // mount new
        patch(null, nextChild, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
    }
    else if (moved) {
        // move if:
        // There is no stable subsequence (e.g. a reverse)
        // OR current node is not among the stable sequence
        if (j < 0 || i !== increasingNewIndexSequence[j]) {
            move(nextChild, container, anchor, 2 /* MoveType.REORDER */);
        }
        else {
            j--;
        }
    }
}

通过getSequence方法获取最长递增子序列increasingNewIndexSequence:
在这里插入图片描述
可以看出索引值有2、3、5是newIndexToOldIndexMap中的最长递增序列,那么这些下标对应的新节点位置不用移动。接下来带大家遍历一下,遍历顺序为倒叙:

  1. 第一个索引是5,key为F,然后取出increasingNewIndexSequence最后一个值5进行对比判断值是否相等,此时相等就不用移动,j–,j为1。
  2. 第二个索引是4,key为g,然后取出increasingNewIndexSequence倒数第二个值3进行对比判断值是否相等,此时不相等则移动真实dom把g插入到F前面。
  3. 第三个索引是3,key为E,然后取出increasingNewIndexSequence倒数第二个值3进行对比判断值是否相等,此时相等就不用移动,j–,j为0。
  4. 第四个索引是2,key为B,然后取出increasingNewIndexSequence倒数第二个值3进行对比判断值是否相等,此时相等就不用移动,j–,j为-1。
  5. 第五个索引是1,key为C,此时j<0直接移动,移动真实dom将C插入到B前面。
  6. 第六个索引是0,key为D,此时j<0直接移动,移动真实dom将D插入到C前面。

在这里插入图片描述

总结

先获取新节点的索引(当前位置),再求出新节点在旧节点中的索引(当前新节点在旧节点中的位置),根据新节点在旧节点中的索引去求最长递增子序列(在旧节点中哪些需要移动,哪些不用移动,这样可以使得移动步骤最少)。然后倒叙遍历新节点,并判断新节点的索引是不是和最长递增子序列的索引相对应,如果相对应则不用移动,否则需要插入。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-11-05 00:19:49  更:2022-11-05 00:22:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 19:23:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码