diff算法-patch函数(一)

本文探讨了diff算法中的patch函数实现,主要讲解如何将新的虚拟节点与旧的虚拟节点进行对比,并通过对比规则进行DOM操作。内容涵盖了从真实DOM到虚拟DOM的转换,以及不同节点类型的处理,如字符串和数组的替换策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

diff算法主要是三步:生成虚拟节点、将新旧虚拟节点对比(核心)、新节点替换旧节点。本篇文章主要讲如何实现patch函数中的其中一部分,因为patch内容较多,可以由浅入深的实现。

介绍

上篇文章实现了h函数,h函数功能是生成虚拟节点,下一步我们就来写patch函数,patch函数的功能就是,拿新的虚拟节点和旧的虚拟节点进行对比,然后通过一系列比较,从而把新的虚拟节点生成为真实的DOM节点,放到页面中,这是patch函数的功能,也是diff算法的核心。

前面我们也介绍了,旧的虚拟节点和新的虚拟节点对比规则有很多,例如:不相同的标签,暴力删除;相同节点不同层级,暴力删除;相同节点,存在key会进行替换等等。我们接下来就由简到深的实现patch函数。

我们先来观察一下snabbdom中的patch是如何使用的

<div id="container">原始数据</div> 

在snabbdom中,patch接收的是两个参数,第一个为旧的真实DOM节点,第二个为新的虚拟DOM节点,但是我们介绍时,是拿新的虚拟节点和旧的虚拟节点进行对比,所以我们的第一步,是将真实DOM转为虚拟DOM,然后再将新的虚拟节点和旧的虚拟节点对比。

实现

  • 创建patch,接收两个参数:新节点和旧节点> 本篇文章主要讨论不是同一个节点的情况,直接暴力删除。> > patch传入新旧节点,并输出

index.js

import h from './handle/h'
import patch from './handle/patch'

const container = document.getElementById('container')


// 第三个参数为字符串的情况
let vnode1 = h('div', {}, 'helloWorld')

// 第三个参数为数组的情况
let vnode2 = h('ul', {}, [h('li', {}, 'a'),h('li', {}, 'b'),h('ul', {}, [h('li', {}, 'c'),h('li', {}, 'd')])
])

patch(container, vnode1)
// patch(container, vnode2) 

patch.js

import vnode from './vnode'
import createElement from './createElement'
export default function (oldVnode, newVnode) {// 判断旧节点是否为真实节点。通过判断旧节点是否有el属性if (oldVnode.el === undefined) {// 将旧真实节点转为旧虚拟节点,通过vnode()oldVnode = vnode(oldVnode.tagName.toLowerCase(), // 获取标签名 el{}, // data[], // childrenundefined, // textoldVnode // 真实节点 elm)}// 判断是否为同一节点,根据新旧节点的sel属性panduan if (oldVnode.sel === newVnode.sel) {// 暂不分析} else { // 不是同一节点,暴力删除// 创建 createElement() ,将新虚拟节点转为 真实DOMlet newVnodeElm = createElement(newVnode)// 获取旧虚拟节点,通过 elm 得到旧真实节点let oldVnodeElm = oldVnode.elm// 创建新节点if (newVnode) {// 根据旧节点的父级节点(body)来插入oldVnodeElm.parentNode.insertBefore(newVnodeElm, oldVnodeElm)}// 删除旧节点oldVnodeElm.parentNode.removeChild(oldVnodeElm)}
} 

vnode.js

export default function (sel, data, children, text, elm) {return {sel,data,children,text,elm}
} 

createElement.js

// 方法接收一个虚拟节点,返回真实DOM
export default function createElement(vnode) {// 根据sel,创建DOM节点let domNode = document.createElement(vnode.sel)// 判断有无子节点,即第三个参数是否为数组if (vnode.children === undefined) {// 无子节点,就是字符串,直接插入domNode.innerText = vnode.text} else if (Array.prototype === vnode.children.__proto__) { // 有children,且为数组// 有子节点,是数组,需要递归for (let child of vnode.children) {// 递归子节点let childDom = createElement(child)domNode.appendChild(childDom)}}// 传入elm属性vnode.elm = domNode// 返回处理好的真实节点return domNode
} 

效果

原始数据:

情况一:字符串替换

情况二:数组替换

总结

当我们创建了新的虚拟节点,需要通过patch函数,将新的虚拟节点替换旧节点,并展示到页面中。主要思路:将旧的真实节点转为旧的虚拟节点,再将新的虚拟节点和旧的虚拟节点进行比较(核心),本篇文章主要对,不同节点进行比较,不同节点时,通过暴力替换,将虚拟节点转为真实节点,此时有两种情况:字符串和数组,字符串直接插入到元素中即可,数组需要递归。最后删除旧节点,放入新节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值