我继承了维护和开发内部日记系统的工作,该系统用于在本地网站上的表中注册库存。这是一个用PHP创建的网站,使用jquery和handontable来列出MySQL数据库中的数据。表格中的所有字段均可由用户编辑。
如今,数据加载可能很慢(在最大的表中为10-15秒),这主要是由于用于填充表和调整列大小的循环所致。
您认为什么是解决此问题的最佳方法?我是否应该通过修复循环来减少加载时间,并保持handontable作为表库?还是应该废弃旧的解决方案并实施新的解决方案?
谢谢:)
答案 0 :(得分:0)
编辑
我刚刚看到您正在使用handontable,所以我的回答并没有真正提供解决方案,因为handsontable已经使用了一种列表虚拟化。我还是留下我的答案
原始答案
您可能可以做的是某种形式的列表虚拟化,尽管对于表格元素来说这可能有些棘手,因为您需要绝对定位和控制高度。通常还假设所有行的高度都相同。
通常的想法是,您只需要渲染屏幕上当前显示的内容即可。假设您随时可以在视口中容纳50行,那么您正在测量和更新650行无关紧要。如果您有50万行,就像在小提琴中一样,那么您的问题将成指数级地失控。
在不知道自己到底在做什么的情况下,这是一种非常通用的解决方法:
var elements = [];
var maxLength = 500000; // Number of elements we're going to generate
var itemHeight = 20; // We need a static row height for this to work
var totalHeight = itemHeight * maxLength; // The total height of the content
var $scrollContainer = $('#scroller-container'); // The container that will scroll
var $scrollContent = $('#scroller-contents'); // The content container for our items.
// We need to set the total height of the content so that absolute positioning works and the container receives the correctly sized scroll bar.
$scrollContent.css({ height: `${totalHeight}px` });
// Generate elements.
for (let i = 0; i < maxLength; i++) {
elements.push({
name: `item_${i}`,
value: `value_${i + 100}`
});
}
// By taking some measurements we will find out
// here exactly what items need to be rendered.
function obtainRenderableItems () {
// The size of our scrollable container
var containerHeight = $scrollContainer.height();
// How many items will fit inside the viewable area of our scrollable container
var viewport_count = Math.ceil(containerHeight / itemHeight);
// Where is it currently scrolled to.
var scrollPosition = $scrollContainer.scrollTop();
// The index of the first item in the viewable area
var start = Math.floor(scrollPosition / itemHeight);
// This calculation gives us a number of items to buffer on either side
// which prevents some janky behaviour when scrolling over yet unrendered items
var preScan = start - viewport_count <= 0 ? 0 : start - viewport_count;
// Basically we get the elements visible on the viewports by the current start
// index, and a buffer at the beginning and the end of the same amount of items
// in the viewport.
return elements.slice(preScan, preScan + (viewport_count * 3)).map((element, index) => {
return [preScan + index, element];
});
};
// Convert it to HTML, you can do whatever here, demo only.
function generateHTML (elements) {
return elements.map(el => {
let div = document.createElement('div');
div.className = 'element';
div.style.height = `${itemHeight}px`;
div.style.top = `${el[0] * itemHeight}px`;
div.innerHTML = `${el[1].name} - ${el[1].value}`;
return div.outerHTML;
}).join('');
}
// When we scroll we recalculate what items need to be shown and rerender them
// inside the page.
function onScroll (event) {
let items = obtainRenderableItems();
let htmlContent = generateHTML(items);
$scrollContent.html(htmlContent);
}
$scrollContainer.scroll(onScroll);
// Run at the beginning
onScroll();
上面的jQuery示例基于我为此目的编写的React组件。您必须原谅我多年没有使用过的jQuery。
此方法有许多警告。主要的问题是所有行的行高必须相同,这在许多情况下都不可行。尽管flex模型可以解决这个问题,但它也依赖于固定的容器高度。