Задача: при реєстрації показати «цей email уже зайнятий» ще до відправки форми. Наївне рішення - запит на кожен введений символ - створює три проблеми:
- навантаження: 20 символів - 20 запитів;
- гонка відповідей: відповідь на
olena@може прийти пізніше за відповідь наolena@example.com, і на екрані залишиться неправильний результат; - перевірка незавершеного значення - марна робота.
Рішення - затримка + скасування + перевірка формату:
import { ref, watch, onWatcherCleanup } from 'vue'
const email = ref('')
const status = ref<'idle' | 'checking' | 'free' | 'taken' | 'error'>('idle')
watch(email, (value) => {
if (!/^\S+@\S+\.\S+$/.test(value)) {
status.value = 'idle'
return
}
const controller = new AbortController()
const timer = setTimeout(async () => {
status.value = 'checking'
try {
const res = await fetch(`/api/email-available?email=${encodeURIComponent(value)}`, {
signal: controller.signal,
headers: { Accept: 'application/json' },
})
status.value = (await res.json()).available ? 'free' : 'taken'
} catch (e) {
if ((e as Error).name !== 'AbortError') status.value = 'error'
}
}, 400)
onWatcherCleanup(() => { // Vue 3.5+: викликається перед наступним запуском
clearTimeout(timer) // ще не почали - не починаємо
controller.abort() // уже почали - скасовуємо запит
})
})
Як це працює: кожна зміна значення спершу «прибирає» попередній запуск - скасовує таймер і запит. Тому відповідь на застаріле значення або не надійде, або буде відкинута, і гонки не буде. До Vue 3.5 те саме робить третій аргумент колбека watch - onCleanup.
Що врахувати:
- 400-500 мс - розумна затримка: користувач устигає дописати слово, і відповідь усе одно здається миттєвою;
AbortError- не помилка, його ігнорують;- перевірка на клієнті - підказка, а не гарантія: між перевіркою й відправкою адресу може зайняти інший користувач. Унікальність гарантує правило
uniqueна сервері й унікальний індекс у базі; - перебір облікових записів: ендпойнт «чи зайнятий email» розкриває, хто зареєстрований. Обмежте частоту запитів (
throttle) і подумайте, чи прийнятне це розкриття для вашого продукту; - у VeeValidate асинхронне правило в схемі запускається при кожній валідації поля - затримку й скасування там теж треба додати, бібліотека не робить їх сама.
Той самий прийом працює для пошуку з автодоповненням, перевірки промокоду, розрахунку вартості доставки.
Докладніше в документації: Vue: очищення побічних ефектів у watch