首页 > 编程语言 >Vue2的diff算法

Vue2的diff算法

时间:2024-03-15 16:33:38浏览次数:16  
标签:node el let Vue2 算法 globalHtml key diff type

export function diff(oldCh, newCh) {
    let oldStartIndex = 0;
    let newStartIndex = 0;
    let oldEndIndex = oldCh.length - 1;
    let oldStartVnode = oldCh[0];
    let oldEndVnode = oldCh[oldEndIndex];
    let newEndIndex = newCh.length - 1;
    let newStartVnode = newCh[0];
    let newEndVnode = newCh[newEndIndex];

    let oldKeyToIndex;
    let i = 1;
    // https://github.com/vuejs/vue/tree/main/src/core/vdom/patch.ts
    while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
        // console.log('循环', newStartIndex, oldStartIndex, newEndIndex, oldEndIndex)
        // console.log('循环体', JSON.parse(JSON.stringify(newCh)), JSON.parse(JSON.stringify(oldCh)))
        // 这一段就是说,从start开始每当一个新节点C复用了旧节点并移动旧节点到C的位置,
        // C旧节点
        if (oldStartVnode === undefined) {
            console.log('第一种')
            oldStartVnode = oldCh[++oldStartIndex] // Vnode has been moved left
        } else if (oldEndVnode === undefined) {
            console.log('第二种')
            oldEndVnode = oldCh[--oldEndIndex]
        }

        else if (sameVNode(oldStartVnode, newStartVnode)) {
            console.log('第三种')
            patchVNode(oldStartVnode, newStartVnode)
            oldStartVnode = oldCh[++oldStartIndex];
            newStartVnode = newCh[++newStartIndex];
        } else if (sameVNode(oldEndVnode, newEndVnode)) {
            console.log('第四种')
            patchVNode(oldEndVnode, newEndVnode)
            oldEndVnode = oldCh[--oldEndIndex];
            newEndVnode = newCh[--newEndIndex];
        } else if (sameVNode(oldStartVnode, newEndVnode)) {
            console.log('第五种')
            patchVNode(oldStartVnode, newEndVnode);
            insertBefore(oldStartVnode, nextSibling(oldEndVnode)) // 移动节点,JS的dom操作方法insertBefore也会是移动的
            oldStartVnode = oldCh[++oldStartIndex];
            newEndVnode = newCh[--newEndIndex];
        } else if (sameVNode(oldEndVnode, newStartVnode)) {
            console.log('第六种')
            patchVNode(oldEndVnode, newStartVnode);
            insertBefore(oldEndVnode, oldStartVnode);
            oldEndVnode = oldCh[--oldEndIndex]
            newStartVnode = newCh[++newStartIndex];
        } else {
            console.log('第七种')
            // 只遍历剩余新节点,oldStartIndex 和 oldEndIndex 保持不变
            // 看当前新节点 是否 和旧节点其中一个一样
            if (oldKeyToIndex === undefined) {
                oldKeyToIndex = createKeyToOldIdx(oldCh, oldStartIndex, oldEndIndex);
            }

            let indexInOld;
            if (newStartVnode.key) {
                indexInOld = oldKeyToIndex.get(newStartVnode.key);
            } else {
                indexInOld = findIndexInOld(newStartVnode);

            }

            if (indexInOld === undefined) {
                // 创建新节点并插入到oldStartVnode前面,
                createElm(newStartVnode, oldStartVnode);
            } else {
                let vnodeToMove = oldCh[indexInOld];

                if (sameVNode(vnodeToMove, newStartVnode)) {
                    patchVNode(vnodeToMove, newStartVnode);
                    oldCh[indexInOld] = undefined;
                    insertBefore(vnodeToMove, oldStartVnode);
                } else {
                    createElm(newStartVnode, oldStartVnode)
                }
            }

            newStartVnode = newCh[++newStartIndex]
        }
    }

    if (oldStartIndex > oldEndIndex) {
        const anchor = newCh[newEndIndex + 1] === undefined ? null : newCh[newEndIndex].el

        addVnodes(anchor, newCh, newStartIndex, newEndIndex)
    } else if (newStartIndex > newEndIndex){
        removeVnodes(oldCh, oldStartIndex, oldEndIndex);
    }
}


function createKeyToOldIdx(oldCh, oldStartIndex, oldEndIndex) {
    const oldKeyToIndex = new Map();
    for (let i = oldStartIndex; i <= oldEndIndex; i++) {
        oldKeyToIndex.set(oldCh[i].key, i);
    }
    return oldKeyToIndex;
}

function findIndexInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx) {
    for (let i = oldStartIdx; i <= oldEndIdx; i++) {
        if (oldCh[i].key === newStartVnode.key) {
            return i;
        }
    }
}


function sameVNode(oldNode, newNode) {
    // 判断VNode是否相等,如果节点类型、组件标识、key相同,就认为是相同的
    // 这里我简化了只比较key,
    // 如果认为是相同就调用patch进一步比较和更新,比如比较属性、事件监听器、子节点比较、组件的状态和props触发组件更新
    if (oldNode.key === newNode.key)
        return true
    else {
        return false
    }
}


function patchVNode(oldNode, newNode, anchor) {
    // 进一步比较和更新,比如比较属性、事件监听器、子节点比较、组件的状态和props触发组件更新
    // 还有更新或替换 dom

    if (oldNode === null) {
        insertBefore(newNode, anchor)
        return;
    }
    if (oldNode.key === newNode.key) {
        replaceChild(newNode, oldNode)
    }
}

function createElm(newStartVnode, anchor) {
    // 实际上是createElement,再insertBefore 或者 appendChild
    // insertBefore 实际插入也只需要一个节点本身,而不是一个索引
    insertBefore(newStartVnode, anchor)
}


function addVnodes(anchor, newCh, startIndex, endIndex) {
    for (let i = endIndex; i >= startIndex; i--) {
        insertBefore(newCh[i], i = endIndex ? anchor : newCh[i+1])
    }
}

function removeVnodes(oldCh, startIndex, endIndex) {
    for (let i = endIndex; i >= startIndex; i--) {
        removeChild(oldCh[i]);
    }
}

// insertBefore(node, child) 将一个节点插入到指定父节点的子列表中的参考节点之前
// 我是为了模拟JS原生方法
// 如果node已经挂载在页面上,insertBefore(node, child)的行为是移动 node 到新的位置,而不是复制它。
function insertBefore(node, anchor) {
    if (anchor === 'tail') {
        appendChild(node)
    } else {
        const oldIndex = globalHtml.findLastIndex(b => b.key === node.key);
        if (oldIndex > -1) {
            globalHtml.splice(oldIndex, 1);
        }
        const index = globalHtml.findLastIndex(b => b.key === anchor.key);
        globalHtml.splice(index, 0, node);
    }
}


// appendChild 将一个节点添加到指定父节点的子列表末尾
function appendChild (node) {
    globalHtml.push(node);
}

// replaceChild 替换父节点的子节点
function replaceChild(newNode, oldNode) {
    const index = globalHtml.findIndex(b => b.key === oldNode.key);
    globalHtml[index].el = newNode.el;
    globalHtml[index].status = '被更新了属性';
}

function nextSibling(node) {
    // 也是为了模拟,实际只需要element.nextElementSibling就能取到element的同级next节点
    const find = globalHtml.findIndex(b => b.key === node.key);
    return globalHtml[find + 1];
}

function removeChild(node) {
    // 这里为了模拟所以需要findIndex,真实dom节点的删除,只需要removeChild(childNode)
    // 不需要index,只需要节点本身
    const index = globalHtml.findIndex(b => b.key === node.key);
    globalHtml.splice(index, 1);
}


export function init(page) {
    globalHtml = page;
}


let oldCh = [
    {key: 1, el: 'a', type: 'old'},
    {key: 2, el: 'b', type: 'old'},
    {key: 3, el: 'c', type: 'old'},
    {key: 4, el: 'd', type: 'old'},
    {key: 5, el: 'e', type: 'old'},
    {key: 6, el: 'f', type: 'old'},
]
let newCh = [
    {key: 3, el: 'c', type: 'new'},
    {key: 4, el: 'd', type: 'new'},
    {key: 2, el: 'b', type: 'new'},
    {key: 1, el: 'a', type: 'new'},
    {key: 6, el: 'f', type: 'new'},
    {key: 7, el: 'g', type: 'new'},
]

let globalHtml = [...oldCh]

init(globalHtml)
diff(oldCh, newCh);
console.log(globalHtml);

  

标签:node,el,let,Vue2,算法,globalHtml,key,diff,type
From: https://www.cnblogs.com/MoisAbby/p/18075728

相关文章

  • 电机参数辨识算法(2)——基于高频注入的磁链辨识策略
    电机参数辨识算法(1)——基于高频注入的电感辨识策略-CSDN博客https://blog.csdn.net/m0_46903653/article/details/136722750?spm=1001.2014.3001.5501上一期已经讲过了电感辨识方法。今天这是参数辨识的第二期,今天来简单看看磁链的辨识。Tpwm=1e-4;%开关周期Tspeed=1e-......
  • 【算法】求 x 的平方根
    leetcode链接题目描述给你一个非负整数x,计算并返回x的算术平方根。由于返回类型是整数,结果只保留整数部分,小数部分将被舍去。注意:不允许使用任何内置指数函数和算符,例如pow(x,0.5)或者x**0.5。示例1:输入:x=4输出:2示例2:输入:x=8输出:2解释:8的......
  • 用A*算法设计搜索策略,补全关于下列走迷宫问题的程序
    补全下列关于走迷宫的程序:classNode():#TODO:完成结点类的定义,结点中要包含状态、父结点、算符等必要成员。根据算法需求,还可能包含该结点的路径代价、启发函数值、估计代价等信息def__init__(self,state,parent,action,stepCost,hCost):self.st......
  • 代码随想录算法训练营第四十七天| ● 198.打家劫舍 ● 213.打家劫舍II ● 337.打家
    打家劫舍 题目链接:198.打家劫舍-力扣(LeetCode)思路:每一家的最大收益来源只有两个,一个是这家不偷,那么最大收益等于从上一家出来的最大收益,另一个是偷这一个家,因此最大收益等于从上上一家出来的最大收益加这一家的收益。classSolution{public:introb(vector<int>&nu......
  • 代码随想录算法训练营第四十七天 | 337.打家劫舍III,213.打家劫舍II ,198.打家劫舍
     198.打家劫舍 已解答中等 相关标签相关企业 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一......
  • Light Random Sprays Retinex 传统的图像增强算法LRSR
    文章目录前言1、LightRandomSpraysRetinex概况2、LightRandomSpraysRetinex具体实现2.1、噪声去除2.2、亮度调整2.3、插值技术3、LightRandomSpraysRetinex源码4、LightRandomSpraysRetinex效果及结论前言  LightRandomSpraysRetinex,即“光随......
  • 基于python+django的协同过滤算法的小说推荐系统
    摘 要随着世界经济信息化、全球网络化的到来推动信息线上管理的飞速发展,为小说推荐的管理起到关件作用。若想达到安全,快捷的目的,就需要拥有信息化的组织和管理模式,建立一套合理、畅通、高效的小说推荐系统,通过此网站爬虫技术获取数据。当前的银行用户行为管理存在工作效率......
  • 聚类算法-K-means
    主要在K-means的理解1介绍K-means算法,以及具体的过程K-means算法是常用的聚类算法之一,属于无监督学习,主要用来将标签未知的数据划分成较少的类/簇,类内的样本差异要小,类间的样本差异要大,这可以帮助我们探索数据结构和分布。K-means的具体实现过程:(四步)初始化模型参数:聚类的簇......
  • 人工智能时代,Java从业者必学科目:数据机构和算法,以及AI算法和技能
    【晋升攻略】Java开发者的AI时代高薪加速器在AI时代,Java从业者必学的科目包括数据结构与算法、AI算法和相关技能,这是因为这些知识和技能是构建和发展人工智能应用的基础。具体分析如下:1.数据结构与算法:数据结构和算法是计算机科学的核心,对于编写高效、可维护的代码至关重......
  • 1秒AI出图的时代来了!Stable Diffusion WebUI Forge+SVD整合包
    速度快N倍!StableDiffusionWebUIForge整合包要说今年绘画圈最大的新秀那妥妥的就StableDiffution本次更新的StableDiffusionWebUIForge整合包+SVD比之前推送的更加智能、快速和简单有多简单呢?这么说吧之前的版本需要初中生级别现在的的幕后网整合包加强版小......