虚拟列表怎么滚动到对应的node节点呢?scrollIntoView仅仅针对界面上DOM已经渲染的情况。
1、先来看看scrollIntoView生效条件
首先要确保 scrollIntoView 在虚拟列表中正确生效,需要确保在调用 scrollIntoView 时目标节点已经被渲染到 DOM中。由于虚拟列表(如 Ant Design 的 Tree 组件)是按需加载节点的,因此直接调用 scrollIntoView 可能会失败
2、再来看看虚拟列表的介绍
虚拟列表是一种优化长列表渲染性能的技术,特别适用于需要展示大量数据的场景。它通过仅渲染用户当前视窗内和附近的元素来减少DOM节点的数量,从而显著提高渲染性能和用户体验。这种技术广泛应用于滚动列表、表格和树形结构等组件中。
3、虚拟列表的核心原理
惰性加载 (Lazy Loading): 只加载和渲染用户视窗内的项目。随着用户滚动,动态加载新的项目并卸载超出视窗范围的项目。 窗口化
(Windowing): 将整个数据集分成多个窗口,每个窗口只包含一部分数据。仅渲染当前视窗及其前后几个窗口的内容。
4、虚拟列表的实现,虚拟列表的实现方式主要包括以下几种
固定高度/宽度: 假设每个列表项的高度或宽度是固定的,利用这个假设快速计算需要渲染的项目。 优点:实现简单,性能好。
缺点:列表项的高度或宽度必须一致。 动态高度/宽度: 列表项的高度或宽度可以不同,需要在滚动时动态计算每个项目的位置和尺寸。
优点:适应不同尺寸的列表项。 缺点:实现复杂度高,性能相对固定高度差一些。
5、常见虚拟列表库
以下是一些常用的虚拟列表库和框架,它们提供了开箱即用的虚拟化解决方案: React Virtualized:
提供了高性能的窗口化和惰性加载解决方案,支持表格、列表、网格等多种组件。 React Window: 比 React Virtualized
更轻量,专注于简单列表和网格的虚拟化。 Ant Design: Ant Design 的 Tree 组件支持虚拟列表,通过 virtual
属性启用。
不绕弯子了, 标题虚拟列表怎么滚动到对应的node节点呢。我们通过计算节点所在的index位置,让虚拟树所在的外层容器滚动index*height即可实现了。
// 平铺展开的节点,找到目标node的下标
const flattenNodes = flattenTreeData(newTreeData, newExpandKeys)
const index = flattenNodes.findIndex(node => {
if (node.key === tableNode.key) {
return true;
}
return false;
});
if (index !== -1) {
const element = document.querySelector(`.x-virtual-list`)
if (element) {
setTimeout(() =>{
element.scrollTop = Math.max(0, (index - 4) * 36);
console.log('scrollContainer.scrollTop:', element.scrollTop);
}, 100)
}
}
// treeNodeList为虚拟树的treeData . expandedKeys为展开了哪些节点
export const flattenTreeData = (treeNodeList = [], expandedKeys = []) => {
const expandedKeySet = new Set(expandedKeys);
const flattenList = [];
const dig = (list, parent = null) => {
return list.map((treeNode, index) => {
// 内部记录的key值,用于记录节点层级关系和位置,拖拽时判断节点位置
const pos = getPosition(parent ? parent.pos : '0', index);
const mergedKey = getKey(treeNode.key, pos);
const flattenNode = {
...treeNode,
parent,
pos,
children: [],
data: treeNode,
};
flattenList.push(flattenNode);
if (expandedKeySet.has(mergedKey)) {
flattenNode.children = dig(treeNode.children || [], flattenNode);
} else {
flattenNode.children = [];
}
return flattenNode;
});
};
dig(treeNodeList);
return flattenList;
};
/**
* 获取节点的位置
*
* @param {string|number} level - 节点层级
* @param {number} index - 节点在层级中的索引
* @returns {string} 节点的位置
*/
export const getPosition = (level, index) => {
return `${level}-${index}`;
};
/**
* 获取节点的键
*
* @param {*} key - 节点的键
* @param {string} pos - 节点的位置
* @returns {*} 节点的键或位置
*/
export const getKey = (key, pos) => {
if (key !== null && key !== undefined) {
return key;
}
return pos;
};```