Форма в браузере представляет собой набор DOM-элементов, состояние которых изменяется через пользовательские действия: ввод текста, переключение чекбоксов, выбор значений, отправка формы. Каждое такое действие генерирует события, которые становятся точками интеграции между пользовательским вводом и логикой валидации.
В контексте Vest обработка событий формы строится вокруг принципа реактивного запуска тестов валидации. Любое изменение состояния формы может запускать пересчёт соответствующих правил, но ключевой особенностью является возможность точечного контроля: какие именно поля проверяются и в какой момент.
Основные типы событий, используемых при работе с формами:
input — изменение значения в реальном времениchange — фиксация изменения значенияblur — потеря фокуса элементомsubmit — попытка отправки формыКаждое из них имеет разную семантику и стоимость с точки зрения производительности и пользовательского опыта.
Vest реализует декларативный подход: правила описываются как набор тестов, сгруппированных по полям. При этом запуск этих тестов обычно привязан к событиям формы.
Типичная стратегия:
onChange → частичная валидация поляonBlur → полная валидация конкретного поляonSubmit → валидация всей формыТакой подход позволяет балансировать между отзывчивостью интерфейса и нагрузкой на выполнение проверок.
Пример базовой структуры валидации:
import { create, test, enforce } from 'vest';
const validationSuite = create((data = {}) => {
test('username', 'Имя пользователя обязательно', () => {
enforce(data.username).isNotEmpty();
});
test('password', 'Пароль слишком короткий', () => {
enforce(data.password).longerThan(5);
});
});
input событий к частичной валидацииСобытие input возникает при каждом изменении значения
поля. Оно обеспечивает максимальную отзывчивость, но требует аккуратного
управления, чтобы избежать избыточных вычислений.
В связке с Vest обычно используется стратегия валидации только текущего поля.
Пример:
function handleInput(field, value, state) {
state[field] = value;
const result = validationSuite(state);
const fieldErrors = result.getErrors(field);
const fieldHasError = fieldErrors.length > 0;
updateUI(field, fieldHasError ? fieldErrors[0] : null);
}
Ключевой момент заключается в том, что полный прогон всей формы может быть избыточным при каждом вводе символа. Vest позволяет извлекать результаты по полю, минимизируя стоимость обновления UI.
change событий как точка фиксации состоянияСобытие change срабатывает реже, чем input,
и чаще используется для:
Валидация на change обычно применяется для полей, где
промежуточные значения не имеют смысла.
Пример обработки:
function handleChange(field, value, state) {
state[field] = value;
const result = validationSuite(state);
const errors = result.getErrors(field);
renderFieldState(field, errors);
}
Здесь важно, что Vest не навязывает стратегию событийной обработки — она полностью определяется уровнем интеграции с UI.
blur для контекстной валидацииСобытие blur является ключевым для UX-ориентированных
форм. Оно позволяет избежать ситуации, когда пользователь видит ошибки
до завершения ввода.
Типичная стратегия:
inputblursubmit как финальную проверкуПример:
function handleBlur(field, state) {
const result = validationSuite(state);
const errors = result.getErrors(field);
setTouched(field, true);
renderFieldState(field, errors);
}
Концепция touched состояния часто используется совместно
с Vest для управления отображением ошибок. Поле считается «проверенным»,
если пользователь с ним взаимодействовал.
submit как централизованной точки проверкиСобытие submit является финальной точкой валидационного
процесса. Здесь выполняется полная проверка всей формы.
Ключевые особенности:
Пример:
function handleSubmit(event, state) {
event.preventDefault();
const result = validationSuite(state);
if (result.hasErrors()) {
const errors = result.getErrors();
renderFormErrors(errors);
return;
}
sendForm(state);
}
Vest здесь выступает как единый источник истины, определяющий корректность всей модели данных.
При интеграции событий формы с Vest важно разделять:
Такое разделение позволяет избежать смешивания UI-логики и бизнес-правил.
Пример структуры состояния:
const state = {
values: {
username: '',
password: ''
},
touched: {
username: false,
password: false
},
errors: {}
};
Vest отвечает исключительно за слой проверки, тогда как управление состоянием событий остаётся на уровне приложения.
Частый вызов suite на каждое событие может быть затратным. Для оптимизации применяются следующие подходы:
Запуск проверки только для конкретного поля:
result.getErrors('username');
inputСнижение частоты вызова:
const debouncedInput = debounce(handleInput, 200);
Предотвращение повторной валидации при отсутствии изменений:
if (prevState[field] === value) return;
Vest возвращает структуру результатов, которая может быть использована напрямую для управления UI.
Основные методы:
result.hasErrors()result.getErrors(field?)result.tested(field)Эти методы позволяют строить реактивную систему отображения ошибок, завязанную на события формы.
Пример синхронизации:
function updateUIFromResult(result) {
Object.keys(state.values).forEach((field) => {
const errors = result.getErrors(field);
renderFieldState(field, errors);
});
}
Комбинация событий форм и Vest позволяет реализовать несколько UX-стратегий:
input + мгновенная проверкаinput без ошибокblur с отображением ошибокsubmit как финальная проверкаsubmitПри использовании компонентов (например, React-подобных абстракций или собственных UI-слоёв) события формы часто проксируются через единый обработчик.
Пример универсального обработчика:
function bindField(field) {
return {
onInput: (e) => handleInput(field, e.target.value, state),
onBlur: () => handleBlur(field, state),
onChange: (e) => handleChange(field, e.target.value, state)
};
}
Такой подход упрощает масштабирование формы, сохраняя централизованную логику взаимодействия с Vest.
Некоторые события формы требуют асинхронной проверки: например, проверка уникальности имени пользователя.
Vest поддерживает асинхронные тесты, которые могут быть интегрированы в событийную модель:
test('username', 'Имя уже занято', async () => {
await checkUsernameAvailability(data.username);
});
Обработка событий в этом случае требует учета:
Ключевой архитектурный принцип заключается в том, что:
Такое разделение позволяет сохранять предсказуемость поведения формы при росте сложности логики и количества полей.