preface

This chapter discusses how to ensure smooth rendering of a page when loading a large amount of data without causing the browser to freeze due to the addition of a large number of DOM nodes.

The instance

For example, to create a web friend list, there may be hundreds or thousands of friends. If a friend is represented by a node, we may need to create n nodes at a time when rendering the list.

<body> <ul id="list-with-big-data">Copy the code

Analysis of the

Insert li into ul dynamically and set the text content of LI.

// Global variables, put here first
let [ary, ul] = [new Array(), document.querySelector('#list-with-big-data')];
// Generate friend data, tentatively 1000
for(i = 1; i < 1000; i++ ){
  ary.push(i)
};
Copy the code

Step1 basic practice

// Basic practice, no fuck said
function renderList() {
  for(let i = 0; i<ary.length; i++) {let li = document.createElement('li');
    li.innerHTML = ary[i];
    ul.appendChild(li)
  }
};
renderList();
Copy the code

The basic approach was fine, but in practice we found that page rendering was slow and stalling due to too much data. Therefore, there is step2.

Step2 is based on time-sharing functions in js higher-order functions. We used renderData method to create nodes in batches. Instead of creating 1000 nodes ata time, we created 8 nodes in 10 milliseconds.

Step2 Time-sharing function

function renderData(fn, count) {
  The renderData method takes two arguments, fn, which is the logical method for creating nodes, and count, which represents the number of nodes created in a batch.
  // Each time count data is traversed, pass in the node to create the function, and delete the data.
  let dataChunk = (a)= > {
    for (i = 0; i < (Math.min(ary.length, count || 0)); i++) {
      let item = ary.shift();
      // Pass each piece of data as an argument to the node-creation function.
      fn(item)
    }
  };
  // The timer is set to 10 milliseconds.
  return (a)= > {
    let timeOut = setInterval(function (params) {
    // The dataChunk method deletes data one by one as it traverses the data, and terminates the timer when the data is empty.
      if (ary.length === 0) {
        return clearInterval(timeOut)
      }
      dataChunk()
    }, 10)}}let renderList = renderData ((n) = > {
  // A simple process of creating and inserting nodes. The default second argument is count 8, and the table renders 8 data at a time.
  let li = document.createElement('li')
  li.innerHTML = n
  ul.appendChild(li)
}, 8)
renderList();
Copy the code

At this point, we are done with batch rendering of data. We can also do some optimizations like requestAnimationFrame + Fragment

step3 DocumentFragment + requestAnimationFrame

// step 3
function renderList(count = 8) {
  let dataChunk = function () {
    // Create a fragment. You can go to Baidu for details. Vue bidirectional binding implementations are also mostly useful.
    let fragment = document.createDocumentFragment();
    for (i = 0; i < (Math.min(ary.length, count || 0)); i++) {
      // same as step2, move the node creation here and delete the data one by one.
      let li = document.createElement('li');
      li.innerHTML = ary[0];
      // Mount the dynamically generated list node to the fragment generated virtual DOM, and then insert it under ul.
      fragment.appendChild(li);
      ary.shift();
     };
     ul.appendChild(fragment);
     renderData()
  };
  var renderData = (a)= > {
    if (ary.length > 0) {
      // The requestAnimationFrame method is called and the dataChunk is passed in as a parameter to recursively render data in and out repeatedly.
      window.requestAnimationFrame(dataChunk)
     }
  }
  renderData();
}
renderList();
Copy the code

The implementation of step3 is based on DocumentFragment. We know that the use of DocumentFragment can reduce the number of DOM operations and the impact of backflow on performance. RequestAniminationFrame can also be used to ensure that the insertion of new nodes is performed before the page is redrawn, and the combination of the two can be optimized for data rendering.

About requestAnimationFrame

[1] requestAnimationFrame consolidates all DOM operations in each frame in a single redraw or reflow, and the redraw or reflow interval closely follows the browser refresh rate. [2] requestAnimationFrame will not be redrawn or reflow in hidden or invisible elements, which of course means less CPU, GPU, and memory usage. [3] requestAnimationFrame is an API specially provided by the browser for animation. At runtime, the browser will automatically optimize method calls and animation will automatically pause if the page is not active, effectively saving CPU overhead