首页 > 解决方案 > 呈现可编辑表格的最佳方式

问题描述

我继承了维护和开发用于在本地网站的表格中登记库存的内部日志系统的工作。这是一个用 PHP 制作的网站,使用 jquery 和 handontable 列出 MySQL 数据库中的数据。表中的所有字段都可由用户编辑。

今天的数据加载可能很慢(在最大的表中需要 10-15 秒),这主要是因为用于填充表和调整列大小的循环。

您认为解决此问题的最佳方法是什么?我是否应该通过修复循环来减少加载时间,并保持可操作性作为表库?还是我应该放弃旧的解决方案并实施新的解决方案?

谢谢 :)

标签: javascriptphpjqueryhandsontable

解决方案


编辑

我刚刚看到您正在使用handsontable,所以我的回答并没有真正提供解决方案,因为handsontable 已经使用了一种列表虚拟化。无论如何我都会留下我的答案


原始答案

您可能可以做的是某种列表虚拟化,尽管这对于表格元素可能有点棘手,因为您需要绝对定位和高度控制。此外,它通常假设所有行都具有相同的高度。

一般的想法是您只想费心渲染当前屏幕上的内容。假设您可以随时将 50 行放入视口中,那么您正在测量和更新 650 行无关紧要的行。如果你有 500000 行,就像小提琴一样,你的问题将成指数地失控。

在不知道您到底在做什么的情况下,这是解决问题的一种非常通用的方法:

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 模型可以解决这个问题。


推荐阅读