Vue3指令之搜索框輸入防抖功能實(shí)現(xiàn)
前言
「搜索??」這個(gè)場景在各種業(yè)務(wù)的系統(tǒng)中都是是非常常見的,比如電商平臺的商品搜索、百度搜索、掘金搜索、google搜索......,只要是有內(nèi)容呈現(xiàn)給用戶的,都少不了搜索功能。
按照觸發(fā)搜索動作的不同可分為:
- 輸完關(guān)鍵字后手動觸發(fā)搜索
- 輸入字符自動搜索
第一種是由用戶去決定何時(shí)進(jìn)行搜索,沒啥問題。
第二種是通過監(jiān)聽用戶錄入的事件自動搜索,自動搜索能在一定程度上提升用戶體驗(yàn)(比如在用戶輸入時(shí)關(guān)鍵詞聯(lián)想提示),同時(shí)也可能會帶來問題,因?yàn)?code>input事件觸發(fā)是非常頻繁的,比如下面這個(gè)示例:
打開百度的搜索,找到輸入框的dom,然后綁定一個(gè)input
事件,看看我們錄入一個(gè)telephone
會觸發(fā)多少次input
事件:
temp1.addEventListener('oninput', (val) => console.log(val))
觸發(fā)了10次:
那如果每觸發(fā)一次都會調(diào)用后臺接口查詢,那當(dāng)用戶量足夠大,就會給后臺服務(wù)器帶來很大的壓力,導(dǎo)致接口響應(yīng)慢,甚至引起服務(wù)器宕機(jī),也會給用戶帶來不好的體驗(yàn)。
為了不引起過多請求給服務(wù)端造成壓力,我們期望在用戶在輸入完以后再發(fā)起請求,那怎么判斷用戶是否輸入完了?
可以認(rèn)為當(dāng)用戶在一個(gè)固定的時(shí)間內(nèi)都沒有錄入字符是輸入完成/暫時(shí)輸入完成。這個(gè)是老生常談的知識點(diǎn)了————「防抖」和「節(jié)流」。掘金有很多文章專門寫這個(gè)的,筆者在此不過多描述,簡單一筆帶過說下筆者的看法。
防抖 & 節(jié)流
防抖
防抖的函數(shù)在調(diào)用后,如果在防抖時(shí)間內(nèi)沒有再次觸發(fā),就會在過了 防抖時(shí)間 后執(zhí)行;如果在防抖時(shí)間內(nèi)再次觸發(fā),不論觸發(fā)多少次,執(zhí)行的時(shí)機(jī)會一直往后延遲,直到滿足最后一次調(diào)用時(shí)間 小于 當(dāng)前時(shí)間減去防抖時(shí)間才會執(zhí)行。
function debounce(fn, time) { let timer return function (...argu) { if (timer) { clearTimeout(timer) timer = null } timer = setTimeout(() => { fn(...argu) clearTimeout(timer) timer = null }, time) } }
節(jié)流
節(jié)流函數(shù)在調(diào)用后,在設(shè)置的節(jié)流時(shí)間后執(zhí)行一次,在此期間,不論觸發(fā)多少次,都直接return
了。
function throttle(fn, time) { let flag = true return function (...argu) { if (!flag) { return } flag = false let timer = setTimeout(() => { fn(...argu) flag = true clearTimeout(timer) timer = null }, time) } }
再來看下加了防抖 & 節(jié)流 處理的input
事件執(zhí)行情況:
防抖:
防抖 + 節(jié)流:
從上面的兩個(gè)圖不難看出防抖 和節(jié)流的區(qū)別,節(jié)流的函數(shù)如果一直被觸發(fā),每隔一段時(shí)間執(zhí)行一次。而防抖則是一直延遲,最后執(zhí)行一次。
輸入防抖存在的問題
上面的防抖函數(shù)執(zhí)行貌似沒啥問題,前提是我們輸入的是英文,如果是中文輸入就出問題了,看下圖:
當(dāng)中文輸入的時(shí)候,即使已經(jīng)防抖處理了,還可能會執(zhí)行多次。接下來咱們進(jìn)入正題了,如何實(shí)現(xiàn)輸入防抖Vue3指令(并解決中文輸入觸發(fā)多次的問題)。
指令實(shí)現(xiàn)
為什么要以指令的方式實(shí)現(xiàn)?我能想到的有以下兩點(diǎn):
- 將防抖處理的邏輯封裝在指令內(nèi)部重用,更易用。
- 指令要支持
input
輸入框,也要支持封裝input
的組件,也就是說需要進(jìn)行底層 DOM 訪問的邏輯
,而這一點(diǎn)恰好是指令提供的能力之一。
需要補(bǔ)充Vue 指令
相關(guān)知識的同學(xué)點(diǎn)這里自定義指令
期望使用方式:
<template> <input v-model="val" v-debounceInput:600="onInput" /> </template> <script setup lang="ts"> import { ref } from 'vue' const val = ref('') function onInput(e: Event): void { console.log(e) } </script>
即指令的綁定值為 執(zhí)行邏輯,指令參數(shù)為 防抖時(shí)長。
指令實(shí)現(xiàn):
import { debounce, isFunction } from '../../utils/index' let inputFunction: (event: Event) => {} // 由于要同時(shí)支持 input 輸入框和封裝 input 的組件,因此需要去找到input這個(gè)元素。 function findInput(el: HTMLElement): HTMLElement | null { const quene: HTMLElement[] = [] quene.push(el) while (quene.length > 0) { const current = quene.shift() if (current?.tagName === 'INPUT') { return current } if (current?.childNodes) { quene.push(...current.childNodes) } } return null } export default { mounted(el: HTMLElement, binding: any) { const { value, arg } = binding if (value && isFunction(value)) { let timeout = 600 if (arg && !Number.isNaN(arg)) { timeout = Number(arg) } inputFunction = debounce(value, timeout) // 執(zhí)行函數(shù)防抖處理 const input = findInput(el) el._INPUT = input if (input) { input.addEventListener('input', inputFunction) } } }, beforeUnmount(el: HTMLElement) { if (el._INPUT) { el._INPUT.removeEventListener('input', inputFunction) el._INPUT = null } } }
接著處理中文輸入可能會觸發(fā)多次的問題??山柚?a rel="external nofollow" target="_blank">compositionstart和compositionend來實(shí)現(xiàn)。
- 輸入法編輯器開始新的輸入合成時(shí)會觸發(fā)
compositionstart
事件。例如,當(dāng)用戶使用拼音輸入法開始輸入漢字時(shí),這個(gè)事件就會被觸發(fā)。 - 當(dāng)文本段落的組成完成或取消時(shí),
compositionend
事件將被觸發(fā) (具有特殊字符的觸發(fā),需要一系列鍵和其他輸入,如語音識別或移動中的字詞建議)。
在一開始拼音輸入法時(shí)就設(shè)置composing
為true,因此在compositionEnd
沒有調(diào)用之前都不會執(zhí)行函數(shù),等compositionEnd
調(diào)用,通過dispatchEvent
派發(fā)一個(gè)input
事件出去,保證邏輯一定會執(zhí)行。
function compositionStart(event: CompositionEvent) { event.target.composing = true } function compositionEnd(e: CompositionEvent) { e.target.composing = false const event = new Event('input', { bubbles: true }) e.target?.dispatchEvent(event) } export default { mounted(el: HTMLElement, binding: any) { //... if (input) { input.addEventListener('input', inputFunction) input.addEventListener('compositionstart', compositionStart) input.addEventListener('compositionend', compositionEnd) } //... }, beforeUnmount(el: HTMLElement) { if (el._INPUT) { el._INPUT.removeEventListener('input', inputFunction) el._INPUT.removeEventListener('compositionstart', compositionStart) el._INPUT.removeEventListener('compositionend', compositionEnd) } } }
防抖實(shí)現(xiàn) & 判斷是 function
export function debounce(input: (event: Event) => any, timeout: number): (this: HTMLElement, ev: Event) => any { let timer: string | number | NodeJS.Timeout | undefined return (event: Event) => { // @ts-ignore if (event.target.composing === true) { return } if (timer) { clearTimeout(timer) timer = undefined } timer = setTimeout(() => { input(event) clearTimeout(timer) timer = undefined }, timeout) } } export function isFunction(param: any): boolean { return Object.prototype.toString.call(param) === '[object Function]' }
注冊指令(全局注冊):
createApp(App).directive('debounceInput', debounceInput).mount('#app')
寫個(gè)案例測試一下:
<template> <div>防抖</div> <input v-model="val" v-debounceInput="onInput" /> <div>普通</div> <input v-model="count" type="text" @input="onInput" /> </template> <script setup lang="ts"> import { ref } from 'vue' const val = ref('') const count = ref('') function onInput(e: Event): void { console.log('調(diào)用了onInput') } </script>
看下最終的效果:
差別還是非常明顯的。指令已發(fā)布npm倉庫,有需要可自取。
總結(jié)
??感謝瀏覽,通過本文你可以了解到以下內(nèi)容:
- 防抖
- 節(jié)流
- Vue3指令開發(fā)
- compositionstart 事件 (需要注意瀏覽器兼容性)
- compositionend 事件(需要注意瀏覽器兼容性)
到此這篇關(guān)于Vue3指令之搜索框輸入防抖功能實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Vue3搜索框輸入防抖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue cli4.0項(xiàng)目引入typescript的方法
這篇文章主要介紹了vue cli4.0項(xiàng)目引入typescript的方法,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07詳解vue中在父組件點(diǎn)擊按鈕觸發(fā)子組件的事件
這篇文章主要介紹了詳解vue中在父組件點(diǎn)擊按鈕觸發(fā)子組件的事件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11vue?懸浮窗且?guī)ё詣游焦δ軐?shí)現(xiàn)demo
這篇文章主要為大家介紹了vue?懸浮窗且?guī)ё詣游焦δ軐?shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06