Валидация в режиме изменения состояния опирается на идею немедленного пересчёта правил при каждом обновлении данных. Подход, реализуемый в Vest, строится вокруг декларативного описания проверок и их автоматического выполнения при изменении значений формы.
Ключевой принцип — привязка результата валидации не к моменту отправки данных, а к моменту их изменения. Это формирует модель, при которой каждое поле становится источником событий для системы проверки.
Основные характеристики:
Внутри Vest логика валидации организована как набор сценариев (suite), содержащих тесты (test). Каждый тест представляет собой атомарное правило, которое либо проходит, либо возвращает ошибку.
Базовая структура:
import { create, test, enforce } from 'vest';
const validation = create((data = {}) => {
test('username', 'Имя обязательно', () => {
enforce(data.username).isNotEmpty();
});
test('email', 'Некорректный email', () => {
enforce(data.email).matches(/.+@.+\..+/);
});
});
Каждый вызов test фиксирует зависимость между полем и
правилом. При повторном запуске изменяются только те тесты, которые
зависят от обновлённых данных.
Валидация на лету строится на привязке функции проверки к обработчикам изменения состояния формы. Обычно это реализуется через события input или change.
let state = {
username: '',
email: ''
};
function onChange(field, value) {
state[field] = value;
validation(state);
}
Каждое изменение вызывает пересчёт сценария, но благодаря внутреннему кэшированию повторно выполняются только затронутые тесты.
Одним из ключевых элементов является механизм предотвращения повторных вычислений. Если входные данные теста не изменились, результат берётся из кэша.
Это даёт следующие эффекты:
Кэширование работает на уровне связки «поле — тест — результат», что позволяет точно определять область повторного выполнения.
Валидация на лету тесно связана с отображением состояния интерфейса. Результаты тестов используются для управления состоянием элементов формы.
Пример интеграции с UI-логикой:
const res = validation(state);
function getError(field) {
return res.getErrors(field)[0];
}
Дальнейшая логика отображения строится вокруг состояния ошибок:
if (getError('email')) {
showError('email', getError('email'));
}
Таким образом, интерфейс становится производным от результата выполнения сценариев.
Сложные формы требуют разделения проверок на логические группы. Vest поддерживает создание отдельных сценариев внутри одного набора валидации.
const validation = create((data = {}) => {
test('username', 'Обязательное поле', () => {
enforce(data.username).isNotEmpty();
});
group('auth', () => {
test('email', 'Email обязателен', () => {
enforce(data.email).isNotEmpty();
});
test('password', 'Слишком короткий пароль', () => {
enforce(data.password).longerThan(6);
});
});
});
Группировка позволяет:
Одной из ключевых возможностей является выполнение тестов только при выполнении условий. Это снижает количество ненужных проверок и повышает предсказуемость результата.
test('company', 'Название компании обязательно', () => {
if (data.isBusiness) {
enforce(data.company).isNotEmpty();
}
});
Условная логика позволяет адаптировать набор проверок под динамическое состояние формы.
Валидация на лету часто включает проверку данных на сервере: уникальность логина, доступность email и т.д. Асинхронные тесты интегрируются в общий поток выполнения.
test('username', 'Имя уже занято', async () => {
const exists = await api.checkUsername(data.username);
enforce(exists).isFalsy();
});
При этом система учитывает:
При каждом обновлении состояния происходит не полный пересчёт всех правил, а частичное обновление зависимых тестов.
Логика работы:
Такой подход критичен для больших форм с десятками полей.
Часть правил зависит сразу от нескольких значений. Например, подтверждение пароля или логические зависимости между полями.
test('confirmPassword', 'Пароли не совпадают', () => {
enforce(data.confirmPassword).equals(data.password);
});
Система отслеживает такие зависимости и автоматически повторяет тест при изменении любого из участвующих полей.
Результат выполнения сценария представляет собой структурированный объект, содержащий ошибки, статус и метаданные выполнения.
Типичная структура:
{
isValid: false,
errors: {
email: ['Некорректный email'],
password: ['Слишком короткий пароль']
}
}
Это состояние используется как единый источник истины для отображения интерфейса и управления логикой отправки данных.
При каждом изменении данных сценарий запускается повторно, но поведение остаётся детерминированным благодаря изоляции тестов.
validation.run(state);
validation.run(state);
Повторные вызовы не приводят к накоплению ошибок или дублированию результатов, так как каждый запуск очищает предыдущее состояние.
Валидация на лету эффективно интегрируется в различные архитектурные подходы:
В каждом случае результат выступает промежуточным слоем между пользовательским вводом и бизнес-логикой обработки данных.
При быстром вводе данных система должна сохранять стабильность. Для этого используются:
Такая модель предотвращает «мигание» ошибок и нестабильное отображение состояния.
При увеличении количества правил важным становится их композиция. Вместо монолитных проверок используются небольшие переиспользуемые функции.
const isRequired = (field) => {
test(field, 'Обязательное поле', () => {
enforce(field).isNotEmpty();
});
};
Далее такие функции комбинируются внутри сценариев, формируя единый слой логики валидации.