在最近,小米9賣的特別火,在官方搶購的時候基本都是一點既空。這不由讓我想到了,官網是怎樣控制顧客不停點擊發起請求而不致使官網崩潰的呢?這由此引出了前端性能優化中的----防抖和節流。在閒聊完後你就會發現有些時候在搶購商品的時候,你用鼠標在幾秒鐘不停的按了數十次,或許它僅僅是發送了你第一次點擊搶購的那個請求。因此說 搶購時間內的第一次點擊尤其關鍵!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="debounce">點我防抖!</button> <script> window.onload = function() { // 一、獲取這個按鈕,並綁定事件 var myDebounce = document.getElementById("debounce"); myDebounce.addEventListener("click", debounce(sayDebounce)); } // 二、防抖功能函數,接受傳參 function debounce(fn) { // 四、建立一個標記用來存放定時器的返回值 let timeout = null; return function() { // 五、每次當用戶點擊/輸入的時候,把前一個定時器清除 clearTimeout(timeout); // 六、而後建立一個新的 setTimeout, // 這樣就能保證點擊按鈕後的 interval 間隔內 // 若是用戶還點擊了的話,就不會執行 fn 函數 timeout = setTimeout(() => { fn.call(this, arguments); }, 1000); }; } // 三、須要進行防抖的事件處理 function sayDebounce() { // ... 有些須要防抖的工做,在這裏執行 console.log("防抖成功!"); } </script> </body> </html>
這是知乎上的一個例子,建立一個定時器,若是在規定時間內重複觸發該事件,就會調用clearTimeout清除掉上一個定時器,重置定時器。也就是說,這件事原本就是須要等待的,並不是當即執行的,若是用戶反覆點擊,那隻好從新等待了。因此,fn.call(this, arguments)
實際上是將不肯定變量替換到函數中了。在這以前已經聊過了arguments,能夠看下我以前寫得 閒聊js中的apply、call和arguments閉包
在上面的這個例子中是非當即執行版。固然,我把知乎的例子修改一下它,將它變成當即執行版。app
<!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() { // 一、獲取這個按鈕,並綁定事件 var myDebounce = document.getElementById("debounce"); myDebounce.addEventListener("click", debounce(sayDebounce)); } // 二、防抖功能函數,接受傳參 function debounce(fn) { // 四、建立一個標記用來存放定時器的返回值 let timeout = null; //5.建立一個判斷是否可點擊值 let doit = true; return function() { // 五、當doit爲真,既用戶重複點擊時,清除定時器 if(doit)clearTimeout(timeout); //6.當doit爲false時,既用戶可點擊,再將doit設爲true,防止用戶重複點擊 else{ fn(); doit = true; } //7.設置定時器,這樣就能保證點擊按鈕後的 interval 間隔內 // 若是用戶還點擊了的話,就不會執行 將doit設爲false函數 timeout = setTimeout(() => { doit = false; }, 1000); }; } // 三、須要進行防抖的事件處理 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() { // 一、獲取按鈕,綁定點擊事件 var myThrottle = document.getElementById("throttle"); myThrottle.addEventListener("click", throttle(sayThrottle)); } // 二、節流函數體 function throttle(fn) { // 四、經過閉包保存一個標記 let canRun = true; return function() { // 五、在函數開頭判斷標誌是否爲 true,不爲 true 則中斷函數 if(!canRun) { return; } // 六、將 canRun 設置爲 false,防止執行以前再被執行 canRun = false; // 七、定時器 setTimeout( () => { fn.call(this, arguments); // 八、執行完事件(好比調用完接口)以後,從新將這個標誌設置爲 true canRun = true; }, 1000); }; } // 三、須要節流的事件 function sayThrottle() { console.log("節流成功!"); } </script> </body> </html>
從這個例子能夠看出,節流能夠防止在某時間間隔內重複發送請求!其和防抖有點類似,但其有本質的區別,雖然都是防止重複觸發事件!
防抖是須要等待多久時間才能再觸發一次事件!
節流是多久時間內只能觸發一次事件!
重繪與迴流
在介紹重繪和迴流以前,最好先了解一下瀏覽器是如何解析解析URL的,或者看一下《瀏覽器渲染頁面過程剖析》
好!如今咱們進入正題 !什麼是重繪和迴流!
重繪(repaint):當元素樣式的改變不影響佈局時,瀏覽器將使用重繪對元素進行更新,此時因爲只須要 UI 層面的從新像素繪製,所以損耗較少。
迴流(reflow):又叫重排(layout)。當元素的尺寸、結構或者觸發某些屬性時,瀏覽器會從新渲染頁面,稱爲迴流。此時,瀏覽器須要從新通過計算,計算後還須要從新頁面佈局,所以是較重的操做。
或許這概念比較抽象,講起來很難理解!簡單點說,就好比咱們頁面中的某些顏色會發生動態改變,而木有影響到尺寸,佈局、位置、結構這些改變的,就叫作重繪,而例如動態添加結點、改變尺寸、位置這些的,就叫作迴流!
迴流的損耗是比較大的!因此儘可能不要產生太多的迴流!就好比,樣式的動態修改不要多步而儘可能應一步到位!
爲了不大量的重繪和迴流!
- 避免頻繁操做樣式,可彙總後統一一次修改
- 儘可能使用 class 進行樣式修改,而不是直接操做樣式
- 減小 DOM 的操做,可以使用字符串一次性插入
迴流一定會觸發重繪,重繪不必定會觸發迴流。重繪的開銷較小,迴流的代價較高。