虚拟dom和diff算法--4(是同一个节点的时候需要精细化比较)

393 阅读3分钟

写在前面,该笔记来自于www.bilibili.com/video/BV1iX… ,侵删

image.png 前面几个步骤已经完成了,现在来进行最后一步,如果新旧节点是同一个节点,那么要进行怎样的精细化比较(所以上一个笔记的patch.js没有写完哈)
下一步要比较的是,两个节点是不是在内存中同一个元素

image.png

image.png 这里感觉有点问题但是实际没问题(因为看弹幕很多同学都觉得有问题,在这说一下自己的理解)要自己注意绕,我们在保证新旧节点都是同一个节点(sel和key一样)之后,再保证他俩不是内存中地址一样的一个对象(假设是A和B,长得一样但是不是一个地址),两个不同的对象都有text,text相同的情况下,是A还是B其实是一样的,因为哪怕你下一次更改,还是要进行比较的,实际效果不会受影响
要注意newVnode要么有text要么有children
还有,假设旧节点本来旧有内容(包含文字和节点),新节点也有,在旧节点上使用innerText更改文字,innerText会全局霸占,完成清空旧的再追加新的文字这个操作
但是假设旧节点有文字的前提下,新节点有子节点,要把新节点的子节点(children追加到旧的上面去,这个时候是不会清空文字的,只会在文字的基础上去追加新的子节点)

image.png 在新老节点都存在children得情况下又有以下情况:
1.新增的情况

image.png 2.删除的情况

image.png 3.更新的情况

image.png 好家伙,这三个算法都不好用,算法太弱,所以去掉不用!

image.png

此时的patchVnode.js

import createElement from './createElement.js'

export default function patchVnode(oldVnode,newVnode){

// 在截至2021.8.10日的最新版snabbdom中,还包含到了新旧节点的属性值是否相同

console.log('是一个节点')

//判断新旧vnode是否是同一个对象

if(oldVnode === newVnode)

return;// 是的话什么都不做

if(newVnode.text != undefined && newVnode.children == undefined || newVnode.children.length == 0){

// 判断newVnode是不是有text属性

console.log('newVnode有text属性,有text属性就一定不会有children')

if(newVnode.text != oldVnode.text){

// 如果新旧虚拟节点的text不同,直接让新的text写入旧的即可。如果老的elm中有children,也会立即消失掉

oldVnode.elm.innerText = newVnode.text

}

}else{

console.log('newVnode没有text属性,也就是说有children')

// 判断oldVnode有没有children属性

if(oldVnode.children != undefined && oldVnode.children.length>0){

// 给所有未处理的节点一个开头

let un =0;

// oldVnode有children,此时新旧节点都有children,是最复杂的情况

for(let i =0;i<newVnode.children.length;i++){

let ch = newVnode.children[i]

// 需要再次遍历,看看oldVnode中有没有节点和它事same的

let isExist = false;

// 是否相同的标识

for(let j =0;j<oldVnode.children.length;j++){

if(oldVnode.children[j].sel == ch.sel && oldVnode.children[j].key == ch.key){

isExist = true

}

}

if(!isExist){

// debugger

// 没有找到,就是说不一样,说明是新节点

let dom = createElement(ch);

ch.elm = dom;

// 上树

if(un<oldVnode.children.length){

oldVnode.elm.insertBefore(dom,oldVnode.children[un].elm)

}else{

oldVnode.elm.appendChild(dom)

}

}else{

// 存在的时候还得判断位置是否需要变动

un++

}

}

}else{

// 老的没有children,新的有children

// 清空老的节点内容

oldVnode.elm.innerText=''

// 遍历新的节点,并且将内容上树

for(let i = 0;i<newVnode.children.length;i++){

let dom =createElement(newVnode.children[i])

oldVnode.elm.appendChild(dom)

}

}

}

}