十分详细的diff算法原理解析

您所在的位置:网站首页 双指针算法原理是什么 十分详细的diff算法原理解析

十分详细的diff算法原理解析

2024-07-10 23:52| 来源: 网络整理| 查看: 265

本文我们总结一下有关diff算法的相关内容和实现原理

开门见山,直接先给出大家diff算法的概念

diff算法可以看作是一种对比算法,对比的对象是新旧虚拟Dom。顾名思义,diff算法可以找到新旧虚拟Dom之间的差异,但diff算法中其实并不是只有对比虚拟Dom,还有根据对比后的结果更新真实Dom。

虚拟Dom

上面的概念我们提到了虚拟Dom,相信大家对这个名词并不陌生,下面为大家解释一下虚拟Dom的概念,以及diff算法中为什么要对比虚拟Dom,而不是直接去操作真实Dom。

虚拟Dom,其实很简单,就是一个用来描述真实Dom的对象

它有六个属性,sel表示当前节点标签名,data内是节点的属性,children表示当前节点的其他子标签节点,elm表示当前虚拟节点对应的真实节点(这里暂时没有),key即为当前节点的key,text表示当前节点下的文本,结构类似这样。

let vnode = { sel: 'ul', data: {}, children: [ { sel: 'li', data: { class: 'item' }, text: 'son1' }, { sel: 'li', data: { class: 'item' }, text: 'son2' }, ], elm: undefined, key: undefined, text: undefined }

那么虚拟Dom有什么用呢。我们其实可以把虚拟Dom理解成对应真实Dom的一种状态。当真实Dom发生变化后,虚拟Dom可以为我们提供这个真实Dom变化之前和变化之后的状态,我们通过对比这两个状态,即可得出真实Dom真正需要更新的部分,即可实现最小量更新。在一些比较复杂的Dom变化场景中,通过对比虚拟Dom后更新真实Dom会比直接更新真实Dom的效率高,这也就是虚拟Dom和diff算法真正存在的意义。

h函数

在介绍diff算法原理之前还需要简单让大家了解一下h函数,因为我们要靠它为我们生成虚拟Dom。这个h函数大家应该也比较熟悉,就是render函数里面传入的那个h函数。

h函数可以接受多种类型的参数,但其实它内部只干了一件事,就是执行vnode函数。根据传入h函数的参数来决定执行vnode函数时传入的参数。那么vnode函数又是干什么的呢?vnode函数其实也只干了一件事,就是把传入h函数的参数转化为一个对象,即虚拟Dom。

// vnode.js export default function (sel, data, children, text, elm) { const key = data.key return {sel, data, children, text, elm, key} }

执行h函数后,内部会通过vnode函数生成虚拟Dom,h函数把这个虚拟在return出去。

diff对比规则

明确了h函数是干什么的,我们可以简单用h函数生成两个不同的虚拟节点,我们将通过一个简易版的diff算法代码介绍diff对比的具体流程。

// 第一个参数是sel 第二个参数是data 第三个参数是children const myVnode1 = h("h1", {}, [ h("p", {key: "a"}, "a"), h("p", {key: "b"}, "b"), ]); ​ const myVnode2 = h("h1", {}, [ h("p", {key: "c"}, "c"), h("p", {key: "d"}, "d"), ]); patch

比较的第一步就是执行patch,它相当于对比的入口。既然是对比两个虚拟Dom,那么就将两个虚拟Dom作为参数传入patch中。patch的主要作用是对比两个虚拟Dom的根节点,并根据对比结果操作真实Dom。

patch函数的核心代码如下,注意注释。

// patch.js ​ import vnode from "./vnode" import patchDetails from "./patchVnode" import createEle from "./createEle" ​ /** * @description 用来对比两个虚拟dom的根节点,并根据对比结果操作真实Dom * @param {*} oldVnode * @param {*} newVnode */ export function patch(oldVnode, newVnode) { // 1.判断oldVnode是否为虚拟节点,不是的话转化为虚拟节点 if(!oldVnode.sel) { // 转化为虚拟节点 oldVnode = vnode(oldVnode.tagName.toLowerCase(), {}, [], undefined, oldVnode) } ​ // 2.判断oldVnode和newVnode是否为同一个节点 if(oldVnode.key == newVnode.key && oldVnode.sel == newVnode.sel) { console.log('是同一个节点') // 比较子节点 patchDetails(oldVnode, newVnode) }else { console.log('不是同一个节点') // 插入newVnode const newNode = createEle(newVnode) // 插入之前需要先将newVnode转化为dom oldVnode.elm.parentNode.insertBefore(newNode, oldVnode.elm) // 插入操作 // 删除oldVnode oldVnode.elm.parentNode.removeChild(oldVnode.elm) } } ​ // createEle.js ​ /** * @description 根据传入的虚拟Dom生成真实Dom * @param {*} vnode * @returns real node */ export default function createEle (vnode) { const realNode = document.createElement(vnode.sel) ​ // 子节点转换 if(vnode.text && (vnode.children == undefined || (vnode.children && vnode.children.length == 0)) ) { // 子节点只含有文本 realNode.innerText = vnode.text }else if(Array.isArray(vnode.children) && vnode.children.length > 0) { // 子节点为其他虚拟节点 递归添加node for(let i = 0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3