vue3利用customRef實(shí)現(xiàn)防抖
防抖你一定學(xué)過,但是你知道如何在 Vue3 里中將防抖做到極致嗎?什么樣的防抖才算是極致呢?
大家好,我是渡一前端子辰老師,首先,讓我們回顧一下防抖的概念。
防抖就是對(duì)于頻繁觸發(fā)的事件添加一個(gè)延時(shí)同時(shí)設(shè)定一個(gè)最小觸發(fā)間隔,如果觸發(fā)間隔小于設(shè)定的間隔,則清除原來的定時(shí),重新設(shè)定新的定時(shí);如果觸發(fā)間隔大于設(shè)定間隔,則保留原來的定時(shí),并設(shè)置新的定時(shí);
防抖的結(jié)果就是頻繁的觸發(fā)轉(zhuǎn)變?yōu)橛|發(fā)一次。
接下來,讓我們看一個(gè)具體的業(yè)務(wù)需求。
在項(xiàng)目中,我們有一個(gè)輸入框,我們希望在用戶輸入后間隔一段時(shí)間再執(zhí)行指定的邏輯。同時(shí),由于防抖需要在項(xiàng)目中多處使用,因此我們希望能夠盡可能方便地重復(fù)使用,并且代碼精簡(jiǎn)。
你可以在這里停一下,自己去嘗試嘗試,子辰在這里等你一會(huì)。
思考
我們先來看一個(gè)簡(jiǎn)單的效果:在輸入框中輸入文本,下方會(huì)顯示我們輸入的文本。
<template> <div class="container"> <input v-model="text" /> <p class="result">{{ text }}</p> </div> </template> <script setup> import { ref } from 'vue'; const text = ref(''); </script>
然后我們現(xiàn)在要加入防抖功能,這意味著數(shù)據(jù)的變化不能立即發(fā)生,而是要等待一小段時(shí)間后再發(fā)生變化。
因此不能再使用 v-model,因?yàn)?v-model 的雙向綁定是數(shù)據(jù)及時(shí)響應(yīng)變化的。
不過我們可以利用 v-model 的特性,將其拆分為 :value=""
和 @input=""
的形式。
<template> <div class="container"> <!-- @input 綁定一個(gè) handleUpdate 函數(shù) --> <input :value="text" @input="handleUpdate" /> <p class="result">{{ text }}</p> </div> </template> <script setup> import { ref } from 'vue'; const text = ref(''); const handleUpdate = (e) => { text.value = e.target.value } </script>
這種形式就是 v-model 的原始形式,效果同 v-model 是一致的。
改成這種寫法之后,我們就可以利用 handleUpdate 函數(shù)進(jìn)行操作了。
因?yàn)檫@里的防抖無非就是讓數(shù)據(jù)的變化延遲執(zhí)行,所以我們可以這樣寫:
<script setup> import { ref } from 'vue'; const text = ref(''); // 我們?cè)谶@里定義一個(gè) timer let timer; const handleUpdate = (e) => { // 每一次執(zhí)行這個(gè)函數(shù)的時(shí)候我們將 timer 情況 clearTimeout(timer) // 然后再執(zhí)行 setTimeout,比如說間隔 1000ms setTimeout(() => { text.value = e.target.value }, 1000) } </script>
/** * @description: 防抖函數(shù) * @param {Function} func: 要延遲執(zhí)行的函數(shù) * @param {Number} delay: 延遲時(shí)間 ( 默認(rèn) 1000ms ) */ export function debounce(func, delay = 1000) { let timer; return function (...args) { clearTimeout(timer); timer = setTimeout(() => { func.call(this, ...args); }, delay); }; }
這樣我們就封裝好了一個(gè)防抖函數(shù)。
現(xiàn)在讓我們?cè)诮M件中使用它。
<script setup> import { ref } from 'vue'; import { debounce } from './debounce'; const text = ref(''); const update = (e) => (text.value = e.target.value) // 將 update 和 延遲時(shí)間傳入進(jìn)去 // 當(dāng)數(shù)據(jù)變化的時(shí)候,運(yùn)行的是防抖函數(shù) // 那么防抖函數(shù)會(huì)隔一段時(shí)間再去運(yùn)行這個(gè) update 函數(shù),去更新數(shù)據(jù) const handleUpdate = debounce(update, 1000) </script>
現(xiàn)在運(yùn)行一下,效果還是沒問題的。
但是現(xiàn)在看起來還是不夠舒服,而且 v-model 原本用得好好地卻被換成了 :value=""
和 @input=""
的形式。
而且每次使用時(shí)還需要寫很多冗余代碼,這我可忍受不了。
但是要繼續(xù)優(yōu)化就需要考驗(yàn)?zāi)芰α?,我們說過我們要做到極致的防抖!
那當(dāng)然要在最初的代碼模式上做防抖才算最完美。
首先讓我們觀察一下 const text = ref('')
得到了什么。我們?cè)诳刂婆_(tái)中輸出 text 看看。
通過輸出結(jié)果可以看出,text 得到了一個(gè)叫做 RefImpl 對(duì)象。
這個(gè)對(duì)象里有一個(gè) value 屬性,當(dāng)你看到 value 屬性是 (...)
時(shí),你應(yīng)該立刻明白它是通過Object.defineProperty
定義的,通過定義之后就會(huì)產(chǎn)生 get 和 set 兩個(gè)函數(shù)。
也就是說,ref 的源代碼看上去就像是 get 和 set 的結(jié)構(gòu)。
根據(jù)上面發(fā)現(xiàn),讓我們寫出 ref 方法的結(jié)構(gòu):
// ref 方法需要一個(gè)值,我們寫作 value function ref(value) { // 它返回一個(gè)對(duì)象 { // 對(duì)象中有一個(gè) value 屬性 // 是通過 Object.defineProperty 定義的 // 定義里會(huì)有一個(gè) get 和 set 方法 get(){ // get 方法返回 value return value }, set(val){ // set 方法給 value重新賦值 value = val } } } // 大概就是這個(gè)樣子 // 但是不要忘記了 vue 是帶有響應(yīng)式的 // 而響應(yīng)式的原理是什么呢? // 就是在 get 的時(shí)候進(jìn)行依賴收集,就是說誰(shuí)用到了我這個(gè)數(shù)據(jù) // 而 set 的時(shí)候叫做派發(fā)更新,表示通知使用數(shù)據(jù)的地方數(shù)據(jù)更新了 // 那么就應(yīng)該是如下的樣子 function ref(value) { { get(){ // 依賴收集,我們稱為 track() return value }, set(val){ // 派發(fā)更新 trigger() value = val } } }
那么這跟我們要做的防抖有什么關(guān)系呢?
再看一下:如果能夠延遲 set 的派發(fā)更新,也就是說將 set 放到一個(gè)防抖函數(shù)中延遲執(zhí)行,那么問題就解決了。
那是否意味著我們要重新寫一個(gè) ref 呢?將其源代碼重新實(shí)現(xiàn)一遍?其實(shí)沒有必要。
因?yàn)?vue 提供了一個(gè)自定義 ref 入口:customRef。
通過 customRef 可以自定義 get 和 set 方法。
那么通過 customRef 我們就看到了勝利的曙光了,讓我們利用它寫出自己的 ref。
實(shí)現(xiàn)
import { customRef } from "vue"; /** * @description: 防抖的 ref * @param {*} value: ref 的 value * @param {*} delay: 延遲時(shí)間,表示 value 的更新要在多少時(shí)間后觸發(fā) ( 默認(rèn)為 1000ms ) */ export function debounceRef(value, delay = 1000) { let timer; // 我們這里利用 customRef 來自定義 ref return customRef(() => { // customRef 中返回一個(gè)對(duì)象,對(duì)象中包含 get 和 set 函數(shù) return { get() { // 依賴收集 track() return value; }, set(val) { // 使用 setTimeout 實(shí)現(xiàn)防抖 clearTimeout(timer); timer = setTimeout(() => { // 派發(fā)更新 trigger() value = val; }, delay); }, }; }); }
上面代碼中我們實(shí)現(xiàn)了自定義 ref,并實(shí)現(xiàn)了防抖功能。
但缺少最關(guān)鍵依賴收集和派發(fā)更新部分,我們沒有時(shí)間重新實(shí)現(xiàn)依賴收集源碼和派發(fā)更新源碼。
但是 vue 貼心的考慮到了這一點(diǎn),它給我們傳入了兩個(gè)參數(shù),track 和 trigger,分別表示依賴收集和派發(fā)更新,我們只要在合適的時(shí)候調(diào)用即可。
import { customRef } from "vue"; export function debounceRef(value, delay = 1000) { let timer; return customRef((track, trigger) => { // 獲得 track, trigger 函數(shù) return { get() { // 依賴收集 track() track(); return value; }, set(val) { clearTimeout(timer); timer = setTimeout(() => { value = val; // 派發(fā)更新 trigger() trigger(); }, delay); }, }; }); }
現(xiàn)在去使用一下,看看效果。
<template> <div class="container"> <input v-model="text" /> <p class="result">{{ text }}</p> </div> </template> <script setup> // 在使用時(shí),我們要引入自定義的 ref import { debounceRef } from './debounceRef'; const text = debounceRef(''); </script>
我們輸出 text 到控制臺(tái)看看。
可以看出得到的是一個(gè) CustomRefImpl 對(duì)象,這個(gè) value 呢,也是一個(gè) get 和 set,只不過現(xiàn)在用的是我們自己寫的 get 和 set。
來試一下效果。
總結(jié)
通過現(xiàn)在的代碼,我們可以輕松實(shí)現(xiàn)防抖的功能,而且使用時(shí)非常簡(jiǎn)單,只需要一個(gè) ref 就可以做到。
這種方式給我們帶來了很大的便利,讓我們?cè)陂_發(fā)過程中更加高效。
以上就是vue3利用customRef實(shí)現(xiàn)防抖的詳細(xì)內(nèi)容,更多關(guān)于vue3 customRef防抖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue開發(fā)chrome插件,實(shí)現(xiàn)獲取界面數(shù)據(jù)和保存到數(shù)據(jù)庫(kù)功能
這篇文章主要介紹了vue開發(fā)chrome插件,實(shí)現(xiàn)獲取界面數(shù)據(jù)和保存到數(shù)據(jù)庫(kù)功能的示例,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-12-12Vue學(xué)習(xí)筆記進(jìn)階篇之vue-cli安裝及介紹
這篇文章主要介紹了Vue學(xué)習(xí)筆記進(jìn)階篇之vue-cli安裝及介紹,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07使用WebStorm運(yùn)行vue項(xiàng)目的詳細(xì)圖文教程
在WebStorm中怎么打開一個(gè)已有的項(xiàng)目,這個(gè)不用多說,那么如何運(yùn)行一個(gè)vue項(xiàng)目呢?下面這篇文章主要給大家介紹了關(guān)于使用WebStorm運(yùn)行vue項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2023-02-02vue項(xiàng)目引入遠(yuǎn)程jweixin-1.2.0.js文件并使用詳解
這篇文章主要介紹了vue項(xiàng)目引入遠(yuǎn)程jweixin-1.2.0.js文件并使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03Vue使用vue-cli創(chuàng)建項(xiàng)目
這篇文章主要介紹了Vue使用vue-cli創(chuàng)建項(xiàng)目,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09vue-router 控制路由權(quán)限的實(shí)現(xiàn)
這篇文章主要介紹了vue-router 控制路由權(quán)限的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09vue3?element?plus按需引入最優(yōu)雅的用法實(shí)例
這篇文章主要給大家介紹了關(guān)于vue3?element?plus按需引入最優(yōu)雅的用法,以及關(guān)于Element-plus按需引入的一些坑,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03Vue-cli3 $ is not defined錯(cuò)誤問題及解決
這篇文章主要介紹了Vue-cli3 $ is not defined錯(cuò)誤問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07