Vue組件中使用防抖和節(jié)流實例分析
在監(jiān)聽頻繁觸發(fā)的事件時,一定要多加小心,比如 用戶在輸入框打字、窗口大小調整、滾動、Intersection Observer 事件。
這些事件總是被頻繁觸發(fā),可能 幾秒一次。如果針對每次事件都發(fā)起 fetch 請求(或類似的行為),那顯然是不明智的。
我們需要做的就是減緩事件處理程序的執(zhí)行速度。這種緩沖技術就是 防抖(debounce) 和 節(jié)流(throttle) 。
1. 觀察者 防抖
我們先從一個簡單的組件開始,我們的任務是 將用戶輸入到 文本框中的文本 輸出到控制臺:
<template> <input v-model="value" type="text" /> <p>{{ value }}</p> </template> <script> export default { data() { return { value: "", }; }, watch: { value(newValue, oldValue) { console.log("Value changed: ", newValue); } } }; </script>
打開demo:
https://codesandbox.io/s/vue-input-szgn1?file=/src/App.vue
打開 demo,在 輸入框 敲幾個字符。每次輸入時,值就會被 log 到控制臺。
我們通過使用 觀察者(watcher) 監(jiān)聽 value 數(shù)據(jù)屬性 來實現(xiàn)了打印日志。但如果你想在 觀察者的回調 中加入一個 使用 value 作為參數(shù) 的 GET 請求,那你應該不會期望太過頻繁地發(fā)起請求。
我們來對 打印控制臺日志 這個行為做一下 防抖。核心思想是創(chuàng)建一個 防抖函數(shù),然后在 觀察者 內部調用該函數(shù)。
我在這里選擇了 'lodash.debounce' 的 防抖實現(xiàn),但你可以自由選擇喜歡的實現(xiàn)方式。
我們來將 防抖邏輯 應用到組件:
<template> <input v-model="value" type="text" /> <p>{{ value }}</p> </template> <script> import debounce from "lodash.debounce"; export default { data() { return { value: "", }; }, watch: { value(...args) { this.debouncedWatch(...args); }, }, created() { this.debouncedWatch = debounce((newValue, oldValue) => { console.log('New value:', newValue); }, 500); }, beforeUnmount() { this.debouncedWatch.cancel(); }, }; </script>
試試 demo
https://codesandbox.io/s/vue-input-debounced-4vwex?file=/src/App.vue
如果你打開這個 demo,你會發(fā)現(xiàn)其實從用戶角度來看,變化不大:你依舊可以像上一個 demo 中一樣自由輸入字符。
但有一個區(qū)別:只有在最后一次輸入的 500ms 之后,才會將新的輸入值打印日志到控制臺。這說明 防抖 在生效。
觀察者 的 防抖實現(xiàn) 只需要 3 個簡單步驟:
在 create() 鉤子 里,創(chuàng)建 防抖回調,并將其賦值到實例上:this.debouncedWatch = debounce(..., 500)。
在 觀察者 回調 watch.value() { ... } 中 傳入正確的參數(shù) 調用 this.debouncedWatch()。
最后,beforeUnmount() 鉤子中 調用 this.debouncedWatch.cancel() ,在卸載組件之前,取消所有還在 pending 的 防抖函數(shù)執(zhí)行。
采用同樣的方式,你可以對任意數(shù)據(jù)屬性的 觀察者 應用 防抖。然后就可以安全執(zhí)行 防抖回調內部的一些比較重的操作,比如 網絡請求、繁重的 DOM 操作,等等。
2. 事件處理器 防抖
上面一節(jié),我展示了如何對 觀察者 使用 防抖,那么常規(guī)的事件處理器呢?
我們重用之前用戶輸入數(shù)據(jù)到輸入框的例子,但這一次會給輸入框加個 事件處理器。
像往常一樣,如果你沒有采取任何緩沖的措施,每當值被修改時,會被打印到控制臺:
<template> <input v-on:input="handler" type="text" /> </template> <script> export default { methods: { handler(event) { console.log('New value:', event.target.value); } } }; </script>
試試 demo:
https://codesandbox.io/s/vue-event-handler-plls4?file=/src/App.vue
打開這個 demo,在輸入框打幾個字符??纯纯刂婆_:你會發(fā)現(xiàn)每次你輸入的時候就會有日志被打印出來。
同樣,如果你會執(zhí)行一些比較重的操作(比如網絡請求),可就不合適了。
對 事件處理器 使用 防抖,可以參考下面這個:
<template> <input v-on:input="debouncedHandler" type="text" /> </template> <script> import debounce from "lodash.debounce"; export default { created() { this.debouncedHandler = debounce(event => { console.log('New value:', event.target.value); }, 500); }, beforeUnmount() { this.debouncedHandler.cancel(); } }; </script>
試試 demo:
https://codesandbox.io/s/vue-event-handler-debounced-973vn?file=/src/App.vue
打開 demo,輸入一些字符。組件只有在最后一次輸入的 500ms 之后,才會將新的輸入值打印日志到控制臺。防抖 再一次生效了!
事件處理器 的 防抖實現(xiàn) 只需要 3 個步驟:
.在 create() 鉤子 里,創(chuàng)建實例后,立刻將 防抖回調 debounce(event => {...}, 500) 賦值到 this.debouncedHandler 。
在輸入框的 template 中 給 v-on:input 賦上 debouncedHandler :
最后,在卸載組件之前, 在 beforeUnmount() 鉤子中 調用 this.debouncedHandler.cancel() ,取消所有還在 pending 的 函數(shù)調用。
另一方面,這些例子應用了 防抖 的技術。然而,同樣的方式可以以用于創(chuàng)建 節(jié)流函數(shù)。
3. 注意
你可能不理解:為什么不直接在 組件的 method 選項中創(chuàng)建 防抖函數(shù),然后在 template 中調用這些方法作為事件處理器?
// ... methods: { // Why not? debouncedHandler: debounce(function () { ... }}, 500) } // ...
這比在實例對象上創(chuàng)建 防抖函數(shù) 要簡單的多。
例如:
<template> <input v-on:input="debouncedHandler" type="text" /> </template> <script> import debounce from "lodash.debounce"; export default { methods: { // Don't do this! debouncedHandler: debounce(function(event) { console.log('New value:', event.target.value); }, 500) } }; </script>
試試 demo
https://codesandbox.io/s/vue-event-handler-debounced-incorrectly-320ci?file=/src/App.vue
這次不是在 created() 鉤子 里創(chuàng)建 防抖回調了,而是將 防抖回調 賦給了 methods.debouncedHandler 。
你如果試過 demo,你會發(fā)現(xiàn)是有效果的!
問題是,組件使用 export default { ... } 導出的 options 對象,包括方法,會被組件實例重用。
如果網頁中有 2 個以上的組件實例,那么所有的組件都會應用 相同 的防抖函數(shù) methods.debouncedHandler — 這會導致防抖出現(xiàn)故障。
4. 總結
在 Vue 中,可以很輕松的對 觀察者 和 事件處理器 應用 防抖 和 節(jié)流。
核心邏輯就是,在 created() 鉤子 里,創(chuàng)建 防抖 或 節(jié)流 的回調,并賦值在實例上。
// ... created() { this.debouncedCallback = debounce((...args) => { // The debounced callback }, 500); }, // ...
A)然后在觀察者內部調用實例上的防抖函數(shù):
// ... watch: { value(...args) { this.debouncedCallback(...args); }, }, // ...
B)或在 template 中設定一個事件處理器:
<template> <input v-on:input="debouncedHandler" type="text" /> </template>
在這之后,每次調用 this.debouncedCallback(...args) ,就算執(zhí)行頻率非常高,內部的回調也能緩沖執(zhí)行。
到此這篇關于Vue組件中使用防抖和節(jié)流實例分析的文章就介紹到這了,更多相關淺析Vue組件使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vuex之this.$store.dispatch()與this.$store.commit()的區(qū)別及說明
這篇文章主要介紹了vuex之this.$store.dispatch()與this.$store.commit()的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06vue組件數(shù)據(jù)傳遞、父子組件數(shù)據(jù)獲取,slot,router路由功能示例
這篇文章主要介紹了vue組件數(shù)據(jù)傳遞、父子組件數(shù)據(jù)獲取,slot,router路由功能,結合實例形式分析了vue.js組件數(shù)據(jù)傳遞、路由相關概念、原理及相關操作技巧,需要的朋友可以參考下2019-03-03element中el-table表頭通過header-row-style設置樣式
有些時候需要給element-ui表頭設置不同樣式,本文主要介紹了element中el-table表頭通過header-row-style設置樣式,具有一定的參考價值,感興趣的可以了解一下2024-01-01element-ui+vue-treeselect下拉框的校驗過程
這篇文章主要介紹了element-ui+vue-treeselect下拉框的校驗過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07