vue組件中節(jié)流函數(shù)的失效的原因和解決方法
今天使用節(jié)流函數(shù)的時(shí)候遇見(jiàn)了一個(gè)問(wèn)題,搞了半天才找到原因,所以在這里做個(gè)總結(jié)。
節(jié)流函數(shù)
瀏覽器的一些事件,如:resize,scroll,mousemove等。這些事件觸發(fā)頻率太過(guò)頻繁,綁定在這些事件上的回調(diào)函數(shù)會(huì)不停的被調(diào)用,加重瀏覽器的負(fù)擔(dān),導(dǎo)致用戶體驗(yàn)非常糟糕。所以先賢們發(fā)明了節(jié)流函數(shù),簡(jiǎn)單版本如下:
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) } }}
假設(shè)有一個(gè) vue 組件 svgMark。這個(gè)組件中渲染的元素要在頁(yè)面窗口大小發(fā)生變化時(shí)重繪 reDraw ,而重繪時(shí)要使用節(jié)流函數(shù)防止性能損耗。正常情況下代碼如下:
<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’, this.reDraw) }, methods: { reDraw: throttle(function() { this.index++ }, 500) }}</script></script>
一般情況下這樣用沒(méi)什么問(wèn)題。但是有這樣一個(gè)場(chǎng)景,使用節(jié)流函數(shù)時(shí)卻失效了,即當(dāng)這個(gè)組件被 v-for 循環(huán)加載了很多次:
<template> <div> <svgMark v-for='item in 10' :key='item.id' /> </div></template>
這個(gè)時(shí)候無(wú)論渲染了多少個(gè) svgMark 組件,在窗口大小改變的時(shí)候卻只觸發(fā)了第一個(gè)組件和第 n 割組件的重繪,為什么其他組件沒(méi)有觸發(fā)呢?這就要從頭說(shuō)起了。
節(jié)流函數(shù)節(jié)流函數(shù)在初始化的時(shí)候產(chǎn)生了一個(gè)閉包,閉包內(nèi)保存了變量 last ,這個(gè) last 記錄了上一次執(zhí)行 f 函數(shù)的時(shí)間。而當(dāng)下一次觸發(fā)節(jié)流函數(shù)的時(shí)候,如果此時(shí)時(shí)間 now 減去上次時(shí)間 last 小于了我們規(guī)定的節(jié)流時(shí)間 wait ,那么函數(shù) f 將不會(huì)執(zhí)行。
很顯然,第一個(gè)子組件在觸發(fā)節(jié)流函數(shù)的時(shí)候產(chǎn)生了一個(gè) last,而第二個(gè)組件在觸發(fā)節(jié)流函數(shù)時(shí)候的時(shí)產(chǎn)生的 now 并沒(méi)有滿足 now - last > wait 的條件,所以沒(méi)有執(zhí)行重繪代碼。而到了第 n 個(gè)組件觸發(fā)節(jié)流函數(shù)的時(shí)候,滿足了 now - last > wait 的條件所以重繪成功了。
vue 組件vue 組件在代碼編譯的階段,組件 svgMark 中的方法 reDraw: throttle(function() { this.index++ }, 500) 就已經(jīng)被編譯成了類(lèi)似如下函數(shù):
reDraw: ƒ (...args) { let now = Date.now() if (now - last > wait) { last = now f.apply(this, args) }}
由于函數(shù)是引用類(lèi)型,所有使用子組件 svgMark 的 methods 中的 reDraw 都指向了同一個(gè)內(nèi)存地址,也就是說(shuō)所有子組件的 reDraw 方法都是同一個(gè)函數(shù)。
因?yàn)樗薪M件都公用了同一個(gè)節(jié)流函數(shù),當(dāng)然就會(huì)產(chǎn)生節(jié)流了。那怎么解決問(wèn)題呢?對(duì)癥下藥就要讓每個(gè)組件產(chǎn)生自己的節(jié)流函數(shù),而不產(chǎn)生共用。代碼如下
子組件:
<template> <div>{{ index }}</div></template><script>import { throttle } from ’lodash’export default { name: ’SvgMark’, data() { return { index: 0 } }, mounted() { this.reDraw = throttle(() => { this.index++ }, 500) window.addEventListener(’resize’, this.reDraw) }, beforeDestroy() { window.removeEventListener(’resize’, this.reDraw) }}</script>
我們?cè)?mounted 聲明周期函數(shù)中手動(dòng)聲明了 reDraw 函數(shù)替代 methods 中的 reDraw ,這樣在每個(gè)組件初始化的時(shí)候都會(huì)產(chǎn)生一個(gè)自己的節(jié)流函數(shù)了。需要注意此時(shí)節(jié)流函數(shù)的參數(shù)使用了箭頭函數(shù),因?yàn)檫@樣 this 才會(huì)指向組件實(shí)例。
以上就是節(jié)流函數(shù)帶給我的坑,現(xiàn)在分享給大家。[下班][鼓掌]
以上就是vue組件中節(jié)流函數(shù)的失效和解決方法的詳細(xì)內(nèi)容,更多關(guān)于vue 組件節(jié)流函數(shù)的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. Intellij IDEA官方最完美編程字體Mono使用2. springboot基于Redis發(fā)布訂閱集群下WebSocket的解決方案3. 關(guān)于探究python中sys.argv時(shí)遇到的問(wèn)題詳解4. 基于android studio的layout的xml文件的創(chuàng)建方式5. CSS自定義滾動(dòng)條樣式案例詳解6. JS繪圖Flot如何實(shí)現(xiàn)動(dòng)態(tài)可刷新曲線圖7. IDEA項(xiàng)目的依賴(pom.xml文件)導(dǎo)入問(wèn)題及解決8. python使用requests庫(kù)爬取拉勾網(wǎng)招聘信息的實(shí)現(xiàn)9. 使用ProcessBuilder調(diào)用外部命令,并返回大量結(jié)果10. Java發(fā)送http請(qǐng)求的示例(get與post方法請(qǐng)求)
