vue diff算法与虚拟dom知识整理(14) patchVNode处理子节点新增和删减

文章详细介绍了如何手动实现Vue的patch过程,包括添加新节点到列表尾部以及删除多余的旧节点。通过调整index.js和updateChildren.js的代码,实现了动态更新DOM时的位置更新和多余节点的管理,同时指出了可能出现的死循环问题及其解决方案。
摘要由CSDN通过智能技术生成

上文
vue diff算法与虚拟dom知识整理(13) 手写patch子节点更新换位策略
我们实现了子节点位置的更新策略
但还有一些匹配不到的情况会导致死循环 那么我们继续来优化一下

我们先将src下的 index.js
代码改成这样

import h from "./snabbdom/h";
import patch from "./snabbdom/patch";

const container = document.getElementById("container");

const vnode = h("section", {}, [
  h("p", {key:"a"}, "a"),
  h("p", {key:"b"}, "b"),
  h("p", {key:"c"}, "c")
]);

patch( container, vnode)

const btn = document.getElementById("btn");
const vnode1 = h("section", {},[
  h("p", {key:"a"}, "a"),
  h("p", {key:"b"}, "b"),
  h("p", {key:"c"}, "c"),
  h("p", {key:"d"}, "d")
]);

btn.onclick = function(){
  patch( vnode, vnode1)
}

我们先来处理 在后面多加入一个节点的逻辑
首先 我们这样写 现在肯定是没有效果的 因为我们并没有写新增的逻辑

我们打开案例中的updateChildren.js
在最上面引入一下createElement

import createElement from "./createElement";

然后 我们在 updateChildren.js 的最后面 加入这段代码

//查看是否还有剩余节点
//判断处理后  新前是否还是比新后小或者二者相同
if (newStartIdx <= newEndIdx) {
    //定义before 复制微新后节点  如果拿不到新后 就直接赋值为unll  因为  insertBefore第二个标杆节点参数给null他就会将节点插入在最后面
    const before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;
    //开启循环 i等于没做完的新前节点  只要i还小于或者等于新后旧继续循环
    for(let i = newStartIdx ; i <= newEndIdx; i++) {
        //用createElement将  newCh(新虚拟节点的集合) 的第i个下标从虚拟节点准成真的个人节点  并插入在before节点的前面
        parentElm.insertBefore(createElement(newCh[i]),before);
    }
}

加到 while 循环的后面
在这里插入图片描述
我们简单判断 如果循环都已经结束了 如果新前还没有加到比新后大 说明 我们的新节点的子节点并没有处理完
我们先获取新后节点
然后 循环遍历 将剩余的新节点都插入到新后后面去
这里 写了个三元运算符 当新后拿不到是 赋值为unll 意思就是 利用了insertBefore第二个参数 如果是unll就自动将元素插在最后面的特性
还有就是createElement 因为 你现在遍历的是虚拟节点 所以 要通过createElement 将虚拟节点变为孤儿节点 然后 插入到dom后面

然后 我们运行项目
在这里插入图片描述
点击更改dom 会发现 我们的d节点就上去了
在这里插入图片描述
但是 如果现在 我们旧节点 如果大于新节点

例如 旧节点的 子节点为 a b c d 新节点 为 a b c 不用试了 我已经帮你们试过了 功能肯定是实现不了的

我们接下来 来处理一下删除的操作
我们先将src下的index.js入口文件改成这样

import h from "./snabbdom/h";
import patch from "./snabbdom/patch";

const container = document.getElementById("container");

const vnode = h("section", {}, [
  h("p", {key:"a"}, "a"),
  h("p", {key:"b"}, "b"),
  h("p", {key:"c"}, "c"),
  h("p", {key:"d"}, "d"),
  h("p", {key:"e"}, "e")
]);

patch( container, vnode)

const btn = document.getElementById("btn");
const vnode1 = h("section", {},[
  h("p", {key:"a"}, "a"),
  h("p", {key:"b"}, "b"),
  h("p", {key:"c"}, "c"),
  h("p", {key:"e"}, "e")
]);

btn.onclick = function(){
  patch( vnode, vnode1)
}

暂时 我们点击肯定是没有什么效果的 因为 我们并没有处理对旧节点进行删除的操作
多出来的旧子节点就还没有得到处理

然后 我们将 updateChildren.js 新增的判断

if (newStartIdx <= newEndIdx) {

下面加一个else if
参考代码如下

//判断旧前还是小于或等于旧后  说明旧节点有多余部分
}else if(oldStartIdx <= oldEndIdx) {
    //定义一个i 接受旧前的值  一直循环到他比旧后大位置  即处理旧前到旧后之间多出来的节点
    for (let i = oldStartIdx; i <= oldEndIdx; i++){
        //将多余部分从dom中删除
        parentElm.removeChild(oldch[i].elm);
    }
}

在这里插入图片描述
这样 我们继续去判断 旧前和旧后 如果旧前没有大于旧后 说明还有节点没处理 即 旧节点有多余的节点
我们直接循环调用removeChild给他们都干掉

这样 我们再运行代码
在这里插入图片描述
点击更改dom
在这里插入图片描述
可以看到 这里这个d就被干掉了

但是目前就还有一个问题
例如 我们将src下的index.js代码修改如下

import h from "./snabbdom/h";
import patch from "./snabbdom/patch";

const container = document.getElementById("container");

const vnode = h("section", {}, [
  h("p", {key:"a"}, "a"),
  h("p", {key:"b"}, "b"),
  h("p", {key:"c"}, "c"),
  h("p", {key:"d"}, "d")
]);

patch( container, vnode)

const btn = document.getElementById("btn");
const vnode1 = h("section", {},[
  h("p", {key:"a"}, "a"),
  h("p", {key:"b"}, "b"),
  h("p", {key:"c"}, "c"),
  h("p", {key:"e"}, "e")
]);

btn.onclick = function(){
  patch( vnode, vnode1)
}

这样运行 你会发现 不但没效果 而且还死循环了
这就是因为旧后和新后没有命中到
这个问题我们后续继续解决

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值