Jquery 一次循环和绑定 10 条记录

Jquery looping and binding 10 records at a time

本文关键字:绑定 记录 循环 一次 Jquery      更新时间:2023-09-26

我有一个场景,我从服务器获取数千条记录作为JSON,并将所有记录绑定到页面。 对于每条记录,我都在jquery中进行一些计算并将数据绑定到UI。由于记录计数为 1000,因此计算和绑定数据所需的时间更多。当所有记录计算完成后,页面上的数据将绑定到末尾。是否有任何选项可以逐个绑定数据或 10 x 10 并在该集的 UI 上显示绑定。我试图找到的是一次为 10 条记录执行 $.each,并将下一组 10 条记录附加到其中,依此类推。有什么想法可以让页面加载得更快吗?(我的要求不需要分页)。任何线索都可以提供帮助。

<div id="keepFinalDataHere"></div>

$.each(data, function (i, record) {
 content += "<div>" + record.id + "</div><div>" + record.fromId + "</div><div>" + record.subject + "</div>";
        });
    $(content).appendTo('#keepFinalDataHere');

在上面的代码中,内容是通过获取数千条记录来构建的,一旦构建了内容,它就会绑定到div。我正在寻找一个选项来获取前 10 个项目绑定数据以确保用户感觉页面已加载,然后将 100 个左右的剩余项目附加到现有列表中。

以简单的方式,您可以分块完成。

 <div id="keepFinalDataHere"></div>
<script>
//.../ 
var chunkSize = 50;//what ever you want or could be dynamic based on data size
var $keepFinalDataHere = $('#keepFinalDataHere');
$.each(data, function (i, record) {
  content += "<div>" + record.id + "</div><div>" + record.fromId + "</div><div>" + record.subject + "</div>";
     if(i % chunkSize === 0){ // content chunk is ready
          $keepFinalDataHere.append(content); // show records
          content = '';//reset the content
         }
    });
if(!(content === '')){//any leftOver records
   $keepFinalDataHere.append(content);
 }

如果要保持 UI 响应,并希望能够在渲染大量 DOM 元素之间执行代码,则必须使用超时机制。为此,可以将 render 方法传递给 setTimeout

setTimeout不是将方法添加到堆栈并立即执行,而是将方法推送到任务队列,并且仅在当前 js 堆栈清除后执行它。

我提出的方法的主要步骤:

  1. 将数据集复制到临时数组
  2. 使用 splice 从数组中删除前 n
  3. 将前 n 项呈现到 DOM
  4. 如果仍有剩余项目,请转到 (2)

这是代码的主要部分,带有注释,假设:

  • testData保存一系列数据点
  • createRow保存将数据点转换为呈现的 DOM 元素的逻辑
  • INITIAL_CHUNK_SIZE保存要在不超时的情况下呈现的行数。
  • DEFAULT_CHUNK_SIZE保存每个后续循环必须呈现的行数

超时渲染器toRenderer ):

var toRenderer = function(s) {
  // We need a copy because `splice` mutates an array
  var dataBuffer = [].concat(testData);
  var nextRender = function(s) {
    // Default value that can be overridden
    var chunkSize = s || DEFAULT_CHUNK_SIZE; 
    dataBuffer
      .splice(0, chunkSize)
      .forEach(createRow);
    if (dataBuffer.length) {
      setTimeout(nextRender);
    }
  };
  // Triggers the initial (not timed out) render
  nextRender(INITIAL_CHUNK_SIZE);
};

在下面的示例中,我包含一个移动微调器,以显示渲染循环如何能够保持适当的帧速率。

请注意,DEFAULT_CHUNK_SIZE越大,渲染所有项目的速度就越快。权衡:一旦一个渲染块需要超过 1/60 秒,您将失去平滑的帧速率。

// SETTINGS
var DATA_LENGTH = 10000;
var DEFAULT_CHUNK_SIZE = 100;
var INITIAL_CHUNK_SIZE = 10;
var list = document.querySelector("ul");
var createRow = function(data) {
  var div = document.createElement("div");
  div.innerHTML = data;
  list.appendChild(div);
};
// Blocking until all rows are rendered
var bruteRenderer = function() {
  console.time("Brute renderer total time:");
  testData.forEach(createRow);
  console.timeEnd("Brute renderer total time:");
}
// Pushes "render assignments" to the "task que"
var toRenderer = function(s) {
  console.time("Timeout renderer total time:");
  var dataBuffer = [].concat(testData);
  var nextRender = function(s) {
    var chunkSize = s || DEFAULT_CHUNK_SIZE;
    dataBuffer
      .splice(0, chunkSize)
      .forEach(createRow);
    if (dataBuffer.length) {
      setTimeout(nextRender);
    } else {
      console.timeEnd("Timeout renderer total time:");
    }
  };
  nextRender(INITIAL_CHUNK_SIZE);
};
// EXAMPLE DATA, EVENT LISTENERS:
// Generate test data
var testData = (function() {
  var result = [];
  for (var i = 0; i < DATA_LENGTH; i += 1) {
    result.push("Item " + i);
  }
  return result;
}());
var clearList = function() {
  list.innerHTML = "";
};
// Attach buttons
document.querySelector(".js-brute").addEventListener("click", bruteRenderer);
document.querySelector(".js-to").addEventListener("click", toRenderer);
document.querySelector(".js-clear").addEventListener("click", clearList);
button {
  display: inline-block;
  margin-right: .5rem;
}
.spinner {
  background: red;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  animation-duration: 1s;
  animation-timing-function: linear;
  animation-direction: alternate;
  animation-name: move;
  animation-iteration-count: infinite;
}
@keyframes move {
  from {
    transform: translate3d(800%, 0, 0);
  }
  to {
    transform: translate3d(0, 0, 0);
  }
}
ul {
  height: 200px;
  overflow-y: scroll;
  background: #efefef;
  border: 1px solid #ccc;
}
<button class="js-brute">
  Inject rows brute force
</button>
<button class="js-to">
  Inject rows timeout
</button>
<button class="js-clear">
  clear list
</button>
<pre></pre>
<div class="spinner"></div>
<ul>
</ul>

如果你对

从服务器获取的数据量有问题,你应该找到一种方法来限制数组。

因此,客户端代码可以只处理适当数量的元素,而只是可以向用户显示的元素。

如果这是不可能的,并且您希望在客户端完成所有操作,则应采用更复杂的方法。

您必须保存指向已处理元素的指针,以及一个包含要处理的元素数量的变量(页码?

然后使用for循环。

// Globally but not global
var cursor = 0
... 

for(var i = cursor; i < (cursor+pageNum); i++) {
    var element = myDataAsJsonFromApi[i];
    // ... do something here.
}
// check if pageNum elements is added..
cursor += pageNum
if (myDataAsJsonFromApi.length == cursor) {
 // load from server...
}

一种选择是将数据缓冲区拆分为块,以便一次可以对某些数据进行操作。

var data = [1,2,3,4,5,6,7,7,8,9,9,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,89];
(function () {
  var lastSliceStart = 0;
  function writeNext() {
    var length = 10;
    var chunk = $(data).slice(lastSliceStart, lastSliceStart+length);
    $(chunk).each((key, item) => {
      console.log(item);
    });
    lastSliceStart += length;
    if (lastSliceStart < data.length) {
      setTimeout(writeNext, 500); // Wait .5 seconds between runs
    }
  }
  writeNext();
})();

https://jsfiddle.net/bogfdmfb/1/

构建队列,一次处理几个项目的队列,显示进度,处理队列中的下一个项目等等。

//your app
App = {
    data: [] //set your JSON dataSource here
}
//define Task
Task = function () {        
    this.buildQueue(10);
};
Task.prototype = {
    buildQueue: function (size) {
        var data_count = App.data.length; //length of your datasource
        this.queue = [];                        
        // fill the queue
        var lastIndex = 0;
        var current_index = size;
        var c = true;
        while (c) {
            if (current_index >= data_count - 1) {
                current_index = data_count;
                c = false;
            }
            this.queue.push([lastIndex, current_index - 1]);
            lastIndex = current_index;
            current_index += size;
        }
        /* If size is 10, array would be [[0,9], [10,19], [20,29]....and so on], The smaller the size, better progress / percentage variation  / loading on ui display */
    },
    doNext: function () {
        if (this.queue.length == 0) {
            this.end();
            return;
        }            
        var row = this.queue.shift(); //stack is LIFO, queue is FIFO, this.queue.pop()
        try {
            this.processQueue(App.data, row[0], row[1]); //pass dataSource, and indexes of array, loop in processQueue function for indexes passed
        } catch (e) {                
            return;
        }
        this.incrementProgress(row[1] / App.data.length); //progress on ui
        // do next
        var _self = this;
        setTimeout(function () {
            _self.doNext();
        }, 1);
    },
    incrementProgress: function (percent) {
        var $progress = $('#percent');
        percent = Math.ceil(percent * 100);
        percent = percent + '%';
        $progress.text(percent);
    },
    start: function () {            
        $('#percent').show();
        this.doNext(); //initiate loop
    },
    end: function () {            
        $('#percent').hide();            
    },
    processQueue: function (data, start, end) {            
        for (var i = start; i <= end; i++) {                
            var dataObj = data[i];
            //use the data here, update UI so user sees something on screen
        }
    }
};
//initialize an instance of Task
var _task = new Task(task); 
_task.start();