In front-end development, what factors can cause page freezes

Front-end development is not like the back-end, there are few scenarios with a large number of algorithms, but front-end performance also needs to be optimized. Good code is the basis for ensuring the stable and high-performance operation of web pages. Combined with the scenarios encountered in previous developments, this article sorts out and analyzes the reasons for the front-end web page freezing, and gives corresponding solutions.

There are many reasons for the front-end page to freeze, which can be divided into two categories from the rendering mechanism and operation, namely:

  • The rendering is not timely, and the page drops frames

  • The memory usage of the webpage is too high, and the operation freezes

The two types can be subdivided as follows:

The rendering is not timely, and the page drops frames

Occupying the js thread for a long time, page reflow and redrawing, many resource loading blocks

Page freeze caused by excessive memory

Memory leaks lead to excessive memory

  • Memory leak caused by unexpected global variable

  • Memory leaks caused by closures

  • forgotten timer

  • circular reference

  • There is no unbind event when DOM is deleted

  • References to DOM elements that are not sanitized

DOM nodes or events occupy too much memory

1. Rendering

1. Occupying the js thread for a long time

The browser includes a js thread and a GUI thread, and the two are mutually exclusive. When the js thread is occupied for a long time, the rendering will not be timely and the page will freeze.

Example 1:

document.body.html('为什么不先渲染我');

//程序
$.ajax({
   url: '',
   async: false
})


//运行结果会在ajax执行完毕后,再去渲染页面

Obtaining data in a synchronous manner will cause the gui thread to hang, and the rendering will be performed after the data returns.

Example 2:

function a (){
    // arr的长度过长时会导致页面卡顿
    arr.forEach(item => {
        ...
    })
}

let inputDom = document.getElementById('input')
let arr = document.getElementsByClassName('.tag')
input.onchange = a

The calculation time is too long and the page rendering is not timely

Reasons for delayed rendering:

The rendering frequency of the browser is generally 60HZ, that is, the time required for one frame is 1s / 60 = 16.67ms. When the browser displays the page, it needs to process js logic and render, and each execution segment cannot exceed 16.67ms. In fact, the operation of the browser kernel's own support system also takes some time, so the time left for us is only about 10ms.

Common optimization methods:

  • Using requestIdleCallback and requestAnimationFrame, task fragmentation

example

function Task(){
    this.tasks = [];
}
//添加一个任务
Task.prototype.addTask = function(task){
    this.tasks.push(task);
};
//每次重绘前取一个task执行
Task.prototype.draw = function(){
    var that = this;
    window.requestAnimationFrame(function(){
        var tasks = that.tasks;        if(tasks.length){
            var task = tasks.shift();
            task();
        }
        window.requestAnimationFrame(function(){that.draw.call(that)});
    });
};

2. There are many page reflows and redraws

  • Minimize the layout

When obtaining dimension attributes such as scrollTop and clentWidth, the layout will be triggered to obtain real-time values, so these values ​​​​should be cached in the for loop

Example:

before optimization

for(var i = 0; i < childs.length; i++){
   childs.style.width = node.offsetWidth + "px";
}

after optimization

var width = node.offsetWidth;for(var i = 0; i < childs.length; i++){
   childs.style.width = width + "px";
}
  • Simplify the DOM structure

When the DOM structure is more complex, the more elements need to be redrawn. So the dom should be kept simple, especially those that are going to be animated, or listen to scroll/mousemove events. In addition, using flex will have advantages in redrawing than using float.

3. Resource loading blocking

  • js resources are placed before the body

  • Inline script blocking

  • CSS loading will block DOM tree rendering (css will not block DOM tree parsing)

  • Too much resource blocking

2. Page freeze caused by excessive memory

1. Memory leaks lead to excessive memory

Browsers have their own set of garbage collection mechanisms. The mainstream garbage collection mechanism is mark clearing. However, accessing native DOM in IE will use a reference counting mechanism. If idle memory is not recovered in time, it will lead to memory leaks.

Briefly introduce the two garbage collection mechanisms (GC Garbage Collection)

Mark Clear:

Definition and usage:

When a variable enters the environment, mark the variable as "entering the environment", and when the variable leaves the environment, mark it as: "leave the environment". At a certain point, the garbage collector will filter out variables in the environment and variables referenced by environment variables, and the rest are variables that are considered ready to be recycled.

So far, the js implementations of IE, Firefox, Opera, Chrome, and Safari all use a mark-and-sweep garbage collection strategy or similar strategies, but the intervals of garbage collection are different from each other.

process:

  • When the browser is running, it will mark all the variables stored in the memory

  • Remove the variables in the environment and the variables referenced in the environment

  • If there are still variables marked, it will be considered as a variable to be deleted

  • The garbage collection mechanism completes the memory clearing work, destroys those marked variables, and reclaims the memory space they occupy

reference count

Definition and Usage : Reference counting is the tracking of the number of times each value is referenced.

Basic principle: It is the number of references to a variable, and it is added by 1 when it is referenced once. When the reference count is 0, it is considered ready to be recycled

Object.

process

  • A variable is declared and a value of a reference type is assigned to the variable. The number of references to this reference type value is 1

  • The same value is assigned to another variable, and the number of value references of this reference type is increased by 1

  • When the variable containing the value of this reference type is assigned another value, then the reference count of the value of this reference type is reduced by 1

  • When the number of references becomes 0, it means that the value needs to be dereferenced

  • When the garbage collection mechanism runs next time, it will free the memory occupied by the value with a reference count of 0

Common causes of memory leaks:

  • Memory leak caused by unexpected global variable

Solution: Avoid using strict mode.

example

<button onclick="createNode()">添加节点</button>
<button onclick="removeNode()">删除节点</button>
<div id="wrapper"></div>
<script>
  var text = [];
  function createNode() { 
      text.push(new Array(1000000).join('x'));  
      var textNode = document.createTextNode("新节点"),
          div = document.createElement('div');
      div.appendChild(textNode);
      document.getElementById("wrapper").appendChild(div);  
  }
  
  function removeNode() {
      var wrapper = document.getElementById("wrapper"),
          len = wrapper.childNodes.length;
      if (len > 0) {
          wrapper.removeChild(wrapper.childNodes[len - 1]);  
      }
  }
  </script>

The text variable is referenced in createNode, so the text cannot be recycled

  • Memory leaks caused by closures

Example:

<button onclick="replaceThing()">第二次点我就有泄漏</button>
  <script>
  var theThing = null;
  var replaceThing = function () {
      var originalThing = theThing;
      var unused = function () {
          if (originalThing) {
              console.log("hi");
          };
      }
      theThing = {
          longStr: new Array(1000000).join('*'),
          someMethod: function someMethod() {
              console.log('someMessage');
          }
      };
  };

The reason the code above leaks is that there are two closures: unused and someMethod , both of which share the parent scope.

Because theThing is a global variable and someMethod is a property of the global variable, the closure scope it references (shared by unused and somMethod) will not be released. Since originalThing is in the shared scope, originalThing will not be released. With replaceThing Continuously calling, originalThing points to the previous theThing, and the new theThing.someMethod will refer to originalThing, thus forming a closure reference chain, and longStr is a large string that cannot be released, resulting in a memory leak.

Solution: add originalThing = null at the end of replaceThing

  • forgotten timer

example

var someResource = getData(); 
setInterval(function() { 
    var node = document.getElementById('Node'); 
    if(node) { 
        // 处理 node 和 someResource 
        node.innerHTML = JSON.stringify(someResource)); 
    } 
}, 1000);

The timer callback function is not recycled (the timer will be recycled when it stops)

  • circular reference

A circular reference is when object A contains another pointer to object B, which also contains a reference to A.

Because the implementation of BOM and DOM in IE uses COM, and the garbage collection mechanism used by COM objects is a reference counting strategy. So there will be a circular reference problem

Solution: Manually disconnect the link between the js object and the DOM. The assigned value is null.

Example:

function handle () {
    var element = document.getElementById(“testId”);
    element.onclick = function (){
        alert(element.id)
    }
}

The properties on the element are referenced in the event bound by the element

The onclick event is a closure, which can maintain local variables in the function so that they cannot be released. That is to say, the element variable cannot be released, and the element will not be released every time it is called, and eventually the memory leaks

Solution:

function handle () {
    var element = document.getElementById(“testId”);
    element.onclick = function (){
        alert(element.id)
    }
    element = null
}
  • There is no unbind event when DOM is deleted

For example, delete a button, but the event on the button is not released

  • References to DOM elements that are not sanitized

2. Dom nodes or events occupy too much memory

For a detailed analysis, see my other article Why do too many DOM elements on a web page cause the page to freeze?

Example:

function addDom(){
        let d = document.createDocumentFragment();
       
        for(var i = 0;i<30;i++){
            let li = document.createElement('li')
            li.addEventListener('click', function(e) {
                let _this = e.target;
                let dom = e.target.tagName.toLowerCase();
                _this.style.color = 'red';
            })
        li.innerHTML = `</div>
            <h4>
                测试图片 
            </h4>
            <img style = "height:20px;width:100px" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591105123139&di=90a63b4962d0b4405ce65c2096a676c2&imgtype=0&src=http%3A%2F%2Fimg0.imgtn.bdimg.com%2Fit%2Fu%3D3769023270%2C3433174748%26fm%3D214%26gp%3D0.jpg"/>
            </div>`
            d.appendChild(li)
        }
        document.getElementById('app').appendChild(d)
    }

The above code is a pull-down loading, and the dom will be added every time, which will eventually lead to excessive memory

The solution: use virtual lists and event delegation

Summary: There are many scenarios for page freezes in the actual development process. You can use the memory leak detection tool (sIEve, for IE) to detect, or you can use the timeline, profiles, or performance provided by chrome, which will not be described in detail here.

Reference: https://www.cnblogs.com/yanglongbo/articles/9762359.html

https://blog.csdn.net/c11073138/article/details/84728132

Guess you like

Origin blog.csdn.net/qdmoment/article/details/106590185
Recommended