在 Vue 中使用lodash對事件進行防抖和節流

點贊再看,養成習慣

本文 GitHub https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。javascript

有些瀏覽器事件能夠在短期內快速觸發屢次,好比調整窗口大小或向下滾動頁面。例如,監聽頁面窗口滾動事件,而且用戶持續快速地向下滾動頁面,那麼滾動事件可能在 3 秒內觸發數千次,這可能會致使一些嚴重的性能問題。css

若是在面試中討論構建應用程序,出現滾動、窗口大小調整或按下鍵等事件請務必說起 防抖(Debouncing) 和 函數節流(Throttling)來提高頁面速度和性能。這兩兄弟的本質都是以閉包的形式存在。經過對事件對應的回調函數進行包裹、以自由變量的形式緩存時間信息,最後用 setTimeout 來控制事件的觸發頻率。前端

Throttle: 第一我的說了算

throttle 的中心思想在於:在某段時間內,無論你觸發了多少次回調,我都只認第一次,並在計時結束時給予響應。java

先給你們講個小故事:如今有一個旅客剛下了飛機,須要用車,因而打電話叫了該機場惟一的一輛機場大巴來接。司機開到機場,心想來都來了,多接幾我的一塊兒走吧,這樣這趟才跑得值——我等個十分鐘看看。因而司機一邊打開了計時器,一邊招呼後面的客人陸陸續續上車。在這十分鐘內,後面下飛機的乘客都只能乘這一輛大巴,十分鐘過去後,無論後面還有多少沒擠上車的乘客,這班車都必須發走。git

在這個故事裏,「司機」 就是咱們的節流閥,他控制發車的時機;「乘客」就是由於咱們頻繁操做事件而不斷涌入的回調任務,它須要接受「司機」的安排;而「計時器」,就是咱們上文提到的以自由變量形式存在的時間信息,它是「司機」決定發車的依據;最後「發車」這個動做,就對應到回調函數的執行。github

總結下來,所謂的「節流」,是經過在一段時間內無視後來產生的回調請求來實現的。只要一位客人叫了車,司機就會爲他開啓計時器,必定的時間內,後面須要乘車的客人都得排隊上這一輛車,誰也沒法叫到更多的車。面試

對應到實際的交互上是同樣同樣的:每當用戶觸發了一次 scroll 事件,咱們就爲這個觸發操做開啓計時器。一段時間內,後續全部的 scroll 事件都會被看成「一輛車的乘客」——它們沒法觸發新的 scroll 回調。直到「一段時間」到了,第一次觸發的 scroll 事件對應的回調纔會執行,而「一段時間內」觸發的後續的 scroll 回調都會被節流閥無視掉。npm

如今一塊兒實現一個 throttle:瀏覽器

// fn是咱們須要包裝的事件回調, interval是時間間隔的閾值
function throttle(fn, interval) {
  // last爲上一次觸發回調的時間
  let last = 0
  
  // 將throttle處理結果看成函數返回
  return function () {
      // 保留調用時的this上下文
      let context = this
      // 保留調用時傳入的參數
      let args = arguments
      // 記錄本次觸發回調的時間
      let now = +new Date()
      
      // 判斷上次觸發的時間和本次觸發的時間差是否小於時間間隔的閾值
      if (now - last >= interval) {
      // 若是時間間隔大於咱們設定的時間間隔閾值,則執行回調
          last = now;
          fn.apply(context, args);
      }
    }
}

// 用throttle來包裝scroll的回調
const better_scroll = throttle(() => console.log('觸發了滾動事件'), 1000)

Debounce: 最後一我的說了算

防抖的中心思想在於:我會等你到底。在某段時間內,無論你觸發了多少次回調,我都只認最後一次。緩存

繼續講司機開車的故事。此次的司機比較有耐心。第一個乘客上車後,司機開始計時(好比說十分鐘)。十分鐘以內,若是又上來了一個乘客,司機會把計時器清零,從新開始等另外一個十分鐘(延遲了等待)。直到有這麼一位乘客,從他上車開始,後續十分鐘都沒有新乘客上車,司機會認爲確實沒有人須要搭這趟車了,纔會把車開走。

咱們對比 throttle 來理解 debounce:在throttle的邏輯裏,「第一我的說了算」,它只爲第一個乘客計時,時間到了就執行回調。而 debounce 認爲,「最後一我的說了算」,debounce 會爲每個新乘客設定新的定時器。

如今一塊兒實現一個 debounce:

// fn是咱們須要包裝的事件回調, delay是每次推遲執行的等待時間
function debounce(fn, delay) {
  // 定時器
  let timer = null
  
  // 將debounce處理結果看成函數返回
  return function () {
    // 保留調用時的this上下文
    let context = this
    // 保留調用時傳入的參數
    let args = arguments

    // 每次事件被觸發時,都去清除以前的舊定時器
    if(timer) {
        clearTimeout(timer)
    }
    // 設立新定時器
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}

// 用debounce來包裝scroll的回調
const better_scroll = debounce(() => console.log('觸發了滾動事件'), 1000)

用 Throttle 來優化 Debounce

debounce 的問題在於它「太有耐心了」。試想,若是用戶的操做十分頻繁——他每次都不等 debounce 設置的 delay 時間結束就進行下一次操做,因而每次 debounce 都爲該用戶從新生成定時器,回調函數被延遲了不可勝數次。頻繁的延遲會致使用戶遲遲得不到響應,用戶一樣會產生「這個頁面卡死了」的觀感。

爲了不弄巧成拙,咱們須要借力 throttle 的思想,打造一個「有底線」的 debounce——等你能夠,但我有個人原則:delay 時間內,我能夠爲你從新生成定時器;但只要delay的時間到了,我必需要給用戶一個響應。這個 throttle 與 debounce 「合體」思路,已經被不少成熟的前端庫應用到了它們的增強版 throttle 函數的實現中:

// fn是咱們須要包裝的事件回調, delay是時間間隔的閾值
function throttle(fn, delay) {
  // last爲上一次觸發回調的時間, timer是定時器
  let last = 0, timer = null
  // 將throttle處理結果看成函數返回
  
  return function () { 
    // 保留調用時的this上下文
    let context = this
    // 保留調用時傳入的參數
    let args = arguments
    // 記錄本次觸發回調的時間
    let now = +new Date()
    
    // 判斷上次觸發的時間和本次觸發的時間差是否小於時間間隔的閾值
    if (now - last < delay) {
    // 若是時間間隔小於咱們設定的時間間隔閾值,則爲本次觸發操做設立一個新的定時器
       clearTimeout(timer)
       timer = setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else {
        // 若是時間間隔超出了咱們設定的時間間隔閾值,那就不等了,不管如何要反饋給用戶一次響應
        last = now
        fn.apply(context, args)
    }
  }
}

// 用新的throttle包裝scroll的回調
const better_scroll = throttle(() => console.log('觸發了滾動事件'), 1000)

document.addEventListener('scroll', better_scroll)

在 Vue 裏使用 lodash 中的 Debouncing 和 Throttling

事件節流和防抖是提升性能或下降網絡開銷的好方法。雖然 Vue 1曾經支持對事件的節流和防抖,可是在Vue 2中爲了保持核心的簡單性,刪除對事件的節流和防抖的支持。所以,在Vue 2對對事件進行防抖和節流咱們可使用 lodash 來作。

安裝

能夠經過 yarn 或 npm 安裝 lodash。

# Yarn
$ yarn add lodash
# NPM
$ npm install lodash --save
注意:若是咱們不想導入 lodash的全部內容,而只導入所需的部分,則能夠經過一些Webpack構建自定義來解決問題。 還可使用 lodash.throttlelodash.debounce等軟件包分別安裝和導入 lodash的各個部分。

throttling 方法

要對事件進行節流處理方法很是簡單,只需將要調用的函數包裝在lodash的_.throttle函數中便可。

<template>
  <button @click="throttledMethod()">Click me as fast as you can!</button>
</template>

<script>
import _ from 'lodash'

export default {
  methods: {
    throttledMethod: _.throttle(() => {
      console.log('I get fired every two seconds!')
    }, 2000)
  }
}
</script>

debouncing 方法

儘管節流在某些狀況下頗有用,但通常狀況咱們常用的是防抖。 防抖實質上將咱們的事件分組在一塊兒,並防止它們被頻繁觸發。 要在Vue組件中使用節流,只需將要調用的函數包裝在lodash的_.debounce函數中。

<template>
  <button @click="throttledMethod()">Click me as fast as you can!</button>
</template>

<script>
import _ from 'lodash'

export default {
  methods: {
    throttledMethod: _.debounce(() => {
      console.log('I only get fired once every two seconds, max!')
    }, 2000)
  }
}
</script>

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug


參考:

Throttling and Debouncing in JavaScript
The Difference Between Throttling and Debouncing
Examples of Throttling and Debouncing
Remy Sharp’s blog post on Throttling function calls
前端性能優化原理與實踐

交流

文章每週持續更新,能夠微信搜索「 大遷世界 」第一時間閱讀和催更(比博客早一到兩篇喲),本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,整理了不少個人文檔,歡迎Star和完善,你們面試能夠參照考點複習,另外關注公衆號,後臺回覆福利,便可看到福利,你懂的。

相關文章
相關標籤/搜索