1. 程式人生 > 程式設計 >vue元件中節流函式的失效的原因和解決方法

vue元件中節流函式的失效的原因和解決方法

今天使用節流函式的時候遇見了一個問題,搞了半天才找到原因,所以在這裡做個總結。

節流函式

瀏覽器的一些事件,如:resize,scroll,mousemove等。這些事件觸發頻率太過頻繁,繫結在這些事件上的回撥函式會不停的被呼叫,加重瀏覽器的負擔,導致使用者體驗非常糟糕。所以先賢們發明了節流函式,簡單版本如下:

function throttle (f,wait = 200) {
 let last = 0
 return function (...args) {
 let now = Date.now()
 if (now - last > wait) {
  last = now
  f.apply(this,args)
 }
 }
}

假設有一個 vue 元件 svgMark。這個元件中渲染的元素要在頁面視窗大小發生變化時重繪 reDraw ,而重繪時要使用節流函式防止效能損耗。正常情況下程式碼如下:

<template>
 <div>{{ index }}</div>
</template>

<script>
import { throttle } from 'lodash'
export default {
 name: 'SvgMark',data() {
 return {
  index: 0
 }
 },mounted() {
 window.addEventListener('resize',this.reDraw)
 },beforeDestroy() {
 window.removeEventListener('resize',methods: {
 reDraw: throttle(function() {
  this.index++
 },500)
 }
}
</script>
</script>

一般情況下這樣用沒什麼問題。但是有這樣一個場景,使用節流函式時卻失效了,即當這個元件被 v-for 迴圈載入了很多次:

<template>
 <div>
 <svgMark v-for="item in 10" :key="item.id" />
 </div>
</template>

這個時候無論渲染了多少個 svgMark 元件,在視窗大小改變的時候卻只觸發了第一個元件和第 n 割元件的重繪,為什麼其他元件沒有觸發呢?這就要從頭說起了。

  • 節流函式

節流函式在初始化的時候產生了一個閉包,閉包內儲存了變數 last ,這個 last 記錄了上一次執行 f 函式的時間。而當下一次觸發節流函式的時候,如果此時時間 now 減去上次時間 last 小於了我們規定的節流時間 wait ,那麼函式 f 將不會執行。

很顯然,第一個子元件在觸發節流函式的時候產生了一個 last,而第二個元件在觸發節流函式時候的時產生的 now 並沒有滿足 now - last > wait 的條件,所以沒有執行重繪程式碼。而到了第 n 個元件觸發節流函式的時候,滿足了 now - last > wait 的條件所以重繪成功了。

  • vue 元件

vue 元件在程式碼編譯的階段,元件 svgMark 中的方法 reDraw: throttle(function() { this.index++ },500) 就已經被編譯成了類似如下函式:

reDraw: ƒ (...args) {
 let now = Date.now()
 if (now - last > wait) {
 last = now
 f.apply(this,args)
 }
}

由於函式是引用型別,所有使用子元件 svgMark 的 methods 中的 reDraw 都指向了同一個記憶體地址,也就是說所有子元件的 reDraw 方法都是同一個函式。

因為所有元件都公用了同一個節流函式,當然就會產生節流了。那怎麼解決問題呢?對症下藥就要讓每個元件產生自己的節流函式,而不產生共用。程式碼如下

子元件:

<template>
 <div>{{ index }}</div>
</template>

<script>
import { throttle } from 'lodash'
export default {
 name: 'SvgMark',mounted() {
 this.reDraw = throttle(() => {
  this.index++
 },500)
 window.addEventListener('resize',this.reDraw)
 }
}
</script>

我們在 mounted 宣告周期函式中手動聲明瞭 reDraw 函式替代 methods 中的 reDraw ,這樣在每個元件初始化的時候都會產生一個自己的節流函數了。需要注意此時節流函式的引數使用了箭頭函式,因為這樣 this 才會指向元件例項。

以上就是節流函式帶給我的坑,現在分享給大家。[下班][鼓掌]

以上就是vue元件中節流函式的失效和解決方法的詳細內容,更多關於vue 元件節流函式的資料請關注我們其它相關文章!