閑聊前端性能----防抖、節流、重繪與回流。


        在最近,小米9賣的特別火,在官方搶購的時候基本都是一點既空。這不禁讓我想到了,官網是怎樣控制顧客不停點擊發起請求而不導致官網崩潰的呢?這由此引出了前端性能優化中的----防抖和節流。在閑聊完后你就會發現有些時候在搶購商品的時候,你用鼠標在幾秒鍾不停的按了數十次,或許它僅僅是發送了你第一次點擊搶購的那個請求。所以說 搶購時間內的第一次點擊尤為關鍵!

 

       下面來介紹一下什么是防抖!

       防抖:任務頻繁觸發的情況下,只有任務觸發的間隔超過制定的時間間隔的時候,任務才會被執行。

下面引用一下知乎上的一個例子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>防抖</title>
</head>
<body>
  <button id="debounce">點我防抖!</button>

  <script>
    window.onload = function() {
      // 1、獲取這個按鈕,並綁定事件
      var myDebounce = document.getElementById("debounce");
      myDebounce.addEventListener("click", debounce(sayDebounce));
    }

    // 2、防抖功能函數,接受傳參
    function debounce(fn) {
      // 4、創建一個標記用來存放定時器的返回值
      let timeout = null;
      return function() {
        // 5、每次當用戶點擊/輸入的時候,把前一個定時器清除
        clearTimeout(timeout);
        // 6、然后創建一個新的 setTimeout,
        // 這樣就能保證點擊按鈕后的 interval 間隔內
        // 如果用戶還點擊了的話,就不會執行 fn 函數
        timeout = setTimeout(() => {
          fn.call(this, arguments);
        }, 1000);
      };
    }

    // 3、需要進行防抖的事件處理
    function sayDebounce() {
      // ... 有些需要防抖的工作,在這里執行
      console.log("防抖成功!");
    }

  </script>
</body>
</html>

       這是知乎上的一個例子,創建一個定時器,如果在規定時間內重復觸發該事件,就會調用clearTimeout清除掉上一個定時器,重置定時器。也就是說,這件事本來就是需要等待的,並非立即執行的,如果用戶反復點擊,那只好重新等待了。所以,fn.call(this, arguments) 其實是將不確定變量替換到函數中了。在這之前已經聊過了arguments,可以看下我之前寫得 閑聊js中的apply、call和arguments

       

       在上面的這個例子中是非立即執行版。當然,我把知乎的例子修改一下它,將它變成立即執行版。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>防抖</title>
</head>
<body>
  <button id="debounce">點我防抖!</button>

  <script>
    window.onload = function() {
      // 1、獲取這個按鈕,並綁定事件
      var myDebounce = document.getElementById("debounce");
      myDebounce.addEventListener("click", debounce(sayDebounce));
    }

    // 2、防抖功能函數,接受傳參
    function debounce(fn) {
      // 4、創建一個標記用來存放定時器的返回值
      let timeout = null;
      //5.創建一個判斷是否可點擊值
      let doit = true;
      return function() {
        // 5、當doit為真,既用戶重復點擊時,清除定時器
        if(doit)clearTimeout(timeout);
        //6.當doit為false時,既用戶可點擊,再將doit設為true,防止用戶重復點擊
        else{
            fn();
            doit = true;
        }
        //7.設置定時器,這樣就能保證點擊按鈕后的 interval 間隔內
        // 如果用戶還點擊了的話,就不會執行 將doit設為false函數
        timeout = setTimeout(() => {
          doit = false;
        }, 1000);
      };
    }

    // 3、需要進行防抖的事件處理
    function sayDebounce() {
      // ... 有些需要防抖的工作,在這里執行
      console.log("防抖成功!");
    }

  </script>
</body>
</html>

其原理和立即執行版本一樣,只是點擊執行的先后順序不同。

 

 

下面 我們來聊一下什么是節流吧!

    節流:指定時間間隔內只會執行一次任務。

這有點像我們刷搶購一樣,當我們在某段時間間隔內觸發了多次事件,其實,它只執行一次請求!

下面 我們再來引用知乎的一個例子就會明白了!

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>節流</title>
</head>
<body>

  <button id="throttle">點我節流!</button>

  <script>
    window.onload = function() {
      // 1、獲取按鈕,綁定點擊事件
      var myThrottle = document.getElementById("throttle");
      myThrottle.addEventListener("click", throttle(sayThrottle));
    }

    // 2、節流函數體
    function throttle(fn) {
      // 4、通過閉包保存一個標記
      let canRun = true;
      return function() {
        // 5、在函數開頭判斷標志是否為 true,不為 true 則中斷函數
        if(!canRun) {
          return;
        }
        // 6、將 canRun 設置為 false,防止執行之前再被執行
        canRun = false;
        // 7、定時器
        setTimeout( () => {
          fn.call(this, arguments);
          // 8、執行完事件(比如調用完接口)之后,重新將這個標志設置為 true
          canRun = true;
        }, 1000);
      };
    }

    // 3、需要節流的事件
    function sayThrottle() {
      console.log("節流成功!");
    }

  </script>
</body>
</html>

從這個例子可以看出,節流可以防止在某時間間隔內重復發送請求!其和防抖有點相似,但其有本質的區別,雖然都是防止重復觸發事件!

防抖是需要等待多久時間才能再觸發一次事件!

節流是多久時間內只能觸發一次事件!

 

重繪與回流

在介紹重繪和回流之前,最好先了解一下瀏覽器是如何解析解析URL的,或者看一下《瀏覽器渲染頁面過程剖析》

好!現在我們進入正題 !什么是重繪和回流!

 

重繪(repaint):當元素樣式的改變不影響布局時,瀏覽器將使用重繪對元素進行更新,此時由於只需要 UI 層面的重新像素繪制,因此損耗較少。

回流(reflow):又叫重排(layout)。當元素的尺寸、結構或者觸發某些屬性時,瀏覽器會重新渲染頁面,稱為回流。此時,瀏覽器需要重新經過計算,計算后還需要重新頁面布局,因此是較重的操作。

 

或許這概念比較抽象,講起來很難理解!簡單點說,就比如我們頁面中的某些顏色會發生動態改變,而木有影響到尺寸,布局、位置、結構這些改變的,就叫做重繪,而例如動態添加結點、改變尺寸、位置這些的,就叫做回流!

回流的損耗是比較大的!所以盡量不要產生太多的回流!就比如,樣式的動態修改不要多步而盡量應一步到位!

為了避免大量的重繪和回流!

  1. 避免頻繁操作樣式,可匯總后統一一次修改
  2. 盡量使用 class 進行樣式修改,而不是直接操作樣式
  3. 減少 DOM 的操作,可使用字符串一次性插入

 

回流必定會觸發重繪,重繪不一定會觸發回流。重繪的開銷較小,回流的代價較高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM