前端優化 —— 函數的節流和防抖

在這裏插入圖片描述

閱讀原文


前言

在前端開發當中咱們常常會綁定一些事件觸發的某些程序執行,有時這些事件會連續觸發,如瀏覽器窗口 scrollresize,輸入框的 keyupinput,以及 click 事件在連續點擊時連續發送請求等等,這些狀況有些會嚴重影響前端性能,有些會增長服務器壓力,使用戶體驗大打折扣,而函數節流和防抖就是爲了解決這樣的問題。html

函數節流 throtter

函數節流:當持續發生事件時,保證在一個固定的時間間隔只執行一次真正的事件處理程序,通俗的說就像 「節流」 的名字同樣,打開水龍頭時要秉承勤儉節約的原則,把閥門關小,最好是達到在固定間隔內水一滴一滴的往下流。前端

一、節流函數的時序圖

在這裏插入圖片描述

從圖中能夠看出,連續觸發事件時,真正執行事件處理程序的間隔是固定的,屢次觸發,也只會在某一個時間間隔內觸發一次,因爲事件處理函數內部執行邏輯各不相同,咱們就封裝一版可通用的節流函數。面試

二、節流函數的封裝

// 文件:throtter.js
// 節流函數
const throtter = (func, delay = 60) => {
    // 鎖的標識
    let lock = false;

    // 返回一個事件處理函數
    return (...args) => {
        // 若是 lock 爲 true 則跳出
        if (lock) return;

        // 執行函數並更改鎖的狀態
        func(...args);
        lock = true;

        // 添加定時器,在到達時間間隔時重置鎖的狀態
        setTimeout(() => lock = false, delay);
    }
}

throtter 函數有兩個參數,第一個參數爲在事件觸發時真正要執行的函數,第二個參數爲定義的間隔時間,在函數執行時定義了 lock 的初始值,經過閉包返回一個函數做爲事件處理函數,在返回的函數內部判斷 lock 狀態並肯定執行真正的函數 func 仍是跳出,每次執行 func 後會更改 lock 狀態,經過定時器在規定的時間間隔內重置 lock,這就是函數節流的原理。瀏覽器

三、驗證節流函數

// 文件:throtter-test.js
// 使用節流函數
document.addEventListener("scroll", throtter(console.log));

上面咱們給 document 對象添加了滾動事件,並不斷的打印事件對象,事件處理函數的默認參數爲事件對象,從執行效果應該能夠看出,平均每 60ms 纔會觸發一次事件,達到了優化性能的目的,若是想讓真正執行的函數 func 傳入更多的參數,只需以下處理。服務器

// 文件:throtter-test.js
// a b 爲函數要傳入的參數
let a = 1, b = 2;

// 返回事件處理函數
const func = throtter(console.log);

// 添加事件監聽
document.addEventListener("scroll", e => func(e, a, b));

節流函數通常用於 scrollresize 事件的狀況較多,由於這些事件的觸發是連續性的,須要在一個時間間隔內只觸發一次。閉包

函數防抖 debounce

函數防抖:當持續發生事件時,事件只在上一次觸發後的一段時間內沒再觸發事件,纔會真正的執行事件處理邏輯,若是每兩次觸發的間隔小於這個時間,則不執行事件邏輯。dom

一、防抖函數的時序圖

在這裏插入圖片描述

從圖中能夠看出,連續觸發事件時並無執行事件處理函數,只有在某一階段連續觸發後的最後一次才執行,也就是上一次觸發的時間間隔要大於設定值才執行,一樣的,事件處理函數內部執行邏輯各不相同,咱們就封裝一版可通用的防抖函數。前端性能

二、防抖函數的封裝

// 文件:debounce.js
// 防抖函數
const dobounce = (func, delay = 300, timer = null) => {
    return (...args) => {
        // 清除定時器
        clearInterval(timer);

        // 在定時器到時後執行事件處理函數
        timer = setTimeout(() => func(...args), delay);
    }
}

dobounce 函數有三個參數,第一個參數爲在事件觸發時真正要執行的函數,第二個參數爲執行事件的延遲時間,第三個參數爲定時器 ID 的初始值,執行 dobounce 經過閉包返回了事件處理函數,在處理函數內部先清除定時器,而後定義定時器並將 ID 賦值給 timer,若是事件連續觸發,則會不斷的清除定時器,直到有一次觸發間隔超過了設定延時時間 delay,纔會真正執行 func函數

三、驗證防抖函數

<!-- 文件:index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函數防抖</title>
</head>
<body>
    <input type="text" id="ipt">
</body>
</html>
// 文件:debounce-test.js
// 使用防抖函數
let ipt = document.querySelector("#ipt");

// 添加事件監聽
ipt.addEventListener("keyup", debounce(console.log));

上面的功能跟 throtter 相似,真正執行事件處理函數時打印事件對象,經過驗證,連續輸入觸發 keyup 事件,上一次觸發和下一次觸發間隔時間必須大於 300ms 纔會執行打印事件對象的邏輯,若是想傳入多個參數套路相同。性能

// 文件:debounce-test.js
// 獲取 dom 元素
let ipt = document.querySelector("#ipt");

// a b 爲函數要傳入的參數
let a = 1, b = 2;

// 返回事件處理函數
const func = debounce(console.log);

// 添加事件監聽
ipt.addEventListener("keyup", e => func(e, a, b));

防抖函數通常用於輸入框事件,經常使用場景就是搜索或查詢,若是不使用防抖會連續發送請求,增長服務器的壓力,使用防抖後,會在用戶輸入要查詢的關鍵詞後才發送請求,這也更符合用戶的習慣,例如百度搜索,就是這樣實現的。

總結

「節流」 和 「防抖」 是前端在項目中常常使用的優化手段,代碼雖然很少,可是確是前端面試 「出鏡率」 很是高的知識點,從而能看出它們的重要性,因此建議前端同窗們必定要知道,並能手寫,這是 「一舉兩得」 的事,能夠用來經過面試,也能夠由於工做中遇到直接就寫而提升工做效率。

相關文章
相關標籤/搜索