[HTML5] Layout Reflow & thrashing

Layout reflow

Layout reflow can be a performance bottleneck. Let's see how to identify it in the browser and what causes the reflow.

In normal process, "Style & Layout" happens after "Javascript".

Here we can consider "Javascript" part is doing "DOM Mutataion" and

"Style & Layout" is doing "DOM Measurement".

"Layout reflow": "Style & Layout" happens during "Javascirpt". Or we can also say "measurement happens after Mutation".

You can identitfy the "Layout reflow":

In this code, it looks like:

function addItem() {
  const newEl = document.createElement("div")
  // Mutate
  wrapper.appendChild(newEl)
  // Measurement
  height = wrapper.getBoundingClientRect().height
}

Solve layout reflow

One possible way is move "Measurement" before "Mutate":

function addItem() {
  const newEl = document.createElement("div")
  // Measurement
  height = wrapper.getBoundingClientRect().height
  // Mutate
  wrapper.appendChild(newEl)
  
}

Second way: if it is not possible to reverse the order of code, we can do: "setTimeout":

function addItem() {
  const newEl = document.createElement("div");
  // Mutate
  wrapper.appendChild(newEl);
  setTimeout(() => {
    // Measurement
    height = wrapper.getBoundingClientRect().height;
  }, 0);
}

Or better, using 'requestAnimationFrame'

function addItem() {
  const newEl = document.createElement("div");
  // Mutate
  wrapper.appendChild(newEl);
  requestAnimationFrame(() => {
    // Measurement
    height = wrapper.getBoundingClientRect().height;
  });
}

Solving layout thrashing by caching measured value

Layout thrashing is when we force the browser to measure the DOM multiple times during a synchronous process. This can have major performance consequences. In this lesson we will see the case in which measuring a static value from the DOM can cause layout thrashing - and it can be solved by caching the value.

In short, do multi layout reflow during one single process

All the red block is showing that there is a big performance issue.

From the code:

    function compareWidths() {
      // repeat "measure" - "mutate" - "measure" -...
      for (let i = 0; i < wrapper.childElementCount; i++) {
        // measure
        const wrapperWidth = wrapper.offsetWidth;
        // mutate
        wrapper.children[i].style.width = wrapperWidth;
      }
    }

We can solve the problem by moving following code out of for loop, because it always getting the same value

const wrapperWidth = wrapper.offsetWidth;
    function compareWidths() {
       // measure
      const wrapperWidth = wrapper.offsetWidth;
      for (let i = 0; i < wrapper.childElementCount; i++) {
        // mutate
        wrapper.children[i].style.width = wrapperWidth;
      }
    }

Another way to solve the problem is using decounce:

const debouncedSubmitNewHeightEvent = _.debounce(submitNewHeightEvent, 50);

So that multi calculation will group into only one calculation.

原文地址:https://www.cnblogs.com/Answer1215/p/12411783.html