skip to content
寻找莉莉丝

新旧节点对比与更新:differNodes

/ 6 min read / 次阅读

一、前言

NodeLib.png

工作中遇到这么一个功能,拖动了一些节点,放置在一个数组 currentNodes 中,然后进行下一步的处理。

当对拖动的那些节点进行删除、复制、撤销、重置等操作时,currentNodes 必然发生一些改变,这些改变可以是:

1. 新节点的添加(新 ID 的加入)

2. 旧节点的更新(版本切换)

3. 旧节点的删除(旧 ID 的删除)

我们需要对比新旧节点列表中的值,当其中的值发生变化时,找出需要删除的节点以及需要添加的节点,由于不变的节点中有我们设置好的配置,因此还要保证这些节点不可发生变动。

  1. 节点的结构如下:
const node = {
	nodeId: "node_1",
	versionId: "v1",
};
  1. 节点列表的结构如下:
const nodeList = [
	{ nodeId: "node_1", versionId: "v1" },
	{ nodeId: "node_2", versionId: "v1" },
	{ nodeId: "node_3", versionId: "v1" },
];

根据这个结构,只要节点的 id 或 versionId 变化时,就更新。

二、情况分析

  1. 怎么处理这个问题?

    准备几个数组:

    • oldList:上一次拖拽产生的节点列表,由 currentNodes 简化复制而来,避免误触原来的配置。
    • updatedList:当前拖拽产生的节点列表,也就是最新的节点列表。
    • addList:新节点的添加或旧节点版本更新时,就把对应节点添加到此数组中。
    • deleteList:在更新的列表中,原来的节点不见了,因为被删除了,找到这些被删除的节点,放进此数组中。

    这个问题的关键在于要把不变的节点信息保留下来,因此不能直接用更新后的节点列表,因为如果直接用更新后的节点列表就会覆盖掉之前配置好的节点。

  2. 具体步骤是什么?

    (1) 利用 JSON.stringify() 方法整体上去对比新旧节点是否一样,如果一样,说明前后一致,没有进行任何操作,currentNodes 没有发生变化。

    (2) 如果不一样,说明节点发生变化。此时就要找出需要添加到 currentNodes 中的新的节点或需要从 currentNodes 中删除的旧的节点。

    • 遍历旧节点列表,找出当前旧节点在新节点列表中对应的相同 id 的节点,然后根据它们的版本 id 不同,进一步处理。如果版本 id 不同,把新版本的节点加入到 addList 中;再把原来的老版本的节点加入到 deleteList 中。

    • 如果在遍历后,没有找到相同 id 的节点,说明这个节点已经被删除了,于是把当前旧节点放进 deleteList 中。

    (3) 除了以上的情况,还有一种情况是:之前基于相同 id 来筛选,对于新的 id 的节点是选不出来的。例如,下图的 c、d 节点。

image-20210901142139132.png 此时,遍历旧节点列表将旧节点id放进 oldNodeIdList 中,然后遍历新节点列表,找出不在 oldNodeIdList 中的节点,这些节点就是新增的节点,需要放进 addList 中。

三、算法

function differNodes(newNodes, oldNodes) {
	const addList = []; // 待添加的节点
	const deleteList = []; // 待删除的节点
	const oldNodeIdList = []; // 上一次节点 id 列表
	const isSame = JSON.stringify(oldNodes) === JSON.stringify(newNodes);
	if (!isSame) {
		oldNodes.forEach((oNode) => {
			const sameIdNode = newNodes.find((nNode) => nNode.nodeId === oNode.nodeId); // 筛选出id相同的节点
			if (sameIdNode) {
				if (sameIdNode.versionId !== oNode.versionId) {
					// 添加新节点
					addList.push(sameIdNode);
					// 删除老节点
					deleteList.push(oNode);
				}
			} else {
				// 没有在新的节点中找到和自己一样id的节点,说明自己被删了
				deleteList.push(oNode);
			}
		});
		// 新节点列表中未检测出的节点放进 addList
		oldNodes.forEach((oNode) => {
			oldNodeIdList.push(oNode.nodeId);
		});
		newNodes.forEach((nNode) => {
			if (!oldNodeIdList.includes(nNode.nodeId)) {
				addList.push(nNode);
			}
		});
	}
	return {
		addList,
		deleteList,
	};
}

四、参考代码

const { log } = console;

let currentNodes = [
	{ nodeId: "node_1", versionId: "v1" },
	{ nodeId: "node_2", versionId: "v1" },
	{ nodeId: "node_3", versionId: "v1" },
];
log("更新前的currentNodes:", currentNodes);

const oldList = [
	{ nodeId: "node_1", versionId: "v1" },
	{ nodeId: "node_2", versionId: "v1" },
	{ nodeId: "node_3", versionId: "v1" },
];
const updatedList = [
	{ nodeId: "node_4", versionId: "v1" },
	{ nodeId: "node_5", versionId: "v1" },
	{ nodeId: "node_6", versionId: "v1" },
];

function differNodes(newNodes, oldNodes) {
	const addList = [];
	const deleteList = [];
	const oldNodeIdList = [];
	const isSame = JSON.stringify(oldNodes) === JSON.stringify(newNodes);
	if (!isSame) {
		oldNodes.forEach((oNode) => {
			const sameIdNode = newNodes.find((nNode) => nNode.nodeId === oNode.nodeId);
			if (sameIdNode) {
				if (sameIdNode.versionId !== oNode.versionId) {
					addList.push(sameIdNode);
					deleteList.push(oNode);
				}
			} else {
				deleteList.push(oNode);
			}
		});
		oldNodes.forEach((oNode) => {
			oldNodeIdList.push(oNode.nodeId);
		});
		newNodes.forEach((nNode) => {
			if (!oldNodeIdList.includes(nNode.nodeId)) {
				addList.push(nNode);
			}
		});
	}
	return {
		addList,
		deleteList,
	};
}

const dealState = differNodes(updatedList, oldList);
const needAddList = dealState.addList;
const needDelList = dealState.deleteList;

needDelList.forEach((item) => {
	const idx = currentNodes.findIndex((node) => node.nodeId === item.nodeId);
	currentNodes.splice(idx, 1);
});

currentNodes.push(...needAddList);

log("更新前的currentNodes:", currentNodes);

CodePen 地址:https://codepen.io/knightdocs/pen/WNOwwgj?editors=0011

以上,感谢您的阅读~