В Yup каждая схема представляет собой объект, содержащий дерево валидаторов и правил трансформации. Создание схемы — относительно дорогая операция, особенно при сложной вложенности и большом количестве полей. Основной принцип оптимизации заключается в том, чтобы избегать повторного создания одинаковых схем.
Наиболее распространённый подход — вынесение схемы на уровень модуля:
import * as yup from 'yup';
export const userSchema = yup.object({
name: yup.string().required(),
email: yup.string().email().required(),
age: yup.number().min(18),
});
Такой подход гарантирует, что схема создаётся один раз при загрузке модуля и далее переиспользуется во всех местах приложения.
Частая ошибка — создание схемы внутри функций компонентов или обработчиков:
function validate(data) {
const schema = yup.object({
name: yup.string().required(),
});
return schema.validate(data);
}
При каждом вызове создаётся новый экземпляр схемы, что приводит к:
Особенно это критично в React-приложениях, где рендеры происходят часто.
В случаях, когда схема зависит от параметров, используется фабрика схем. Для предотвращения повторной генерации применяется мемоизация.
import * as yup from 'yup';
const schemaCache = new Map();
export function getUserSchema(withAge) {
const key = String(withAge);
if (schemaCache.has(key)) {
return schemaCache.get(key);
}
const schema = yup.object({
name: yup.string().required(),
...(withAge ? { age: yup.number().min(18) } : {}),
});
schemaCache.set(key, schema);
return schema;
}
Такой подход позволяет:
Более компактный вариант мемоизации — использование замыканий:
const createSchema = (() => {
let cache = null;
return () => {
if (cache) return cache;
cache = yup.object({
email: yup.string().email().required(),
});
return cache;
};
})();
Такой способ подходит для статических схем, которые не зависят от внешних параметров.
При работе с динамическими конфигурациями удобно использовать
WeakMap, особенно если ключами выступают объекты
конфигурации.
import * as yup from 'yup';
const schemaCache = new WeakMap();
export function getSchema(config) {
if (schemaCache.has(config)) {
return schemaCache.get(config);
}
const schema = yup.object({
name: yup.string().required(config.requiredName),
});
schemaCache.set(config, schema);
return schema;
}
Преимущества:
Yup поддерживает динамическую генерацию схем через lazy
и when. Эти механизмы часто используются вместо ручного
кеширования.
const schema = yup.lazy((value) => {
if (typeof value === 'string') {
return yup.string().min(3);
}
return yup.object({
id: yup.number().required(),
});
});
Каждый вызов может создавать новую ветку схемы, поэтому при сложной логике важно контролировать повторное использование возвращаемых структур.
const schema = yup.object({
type: yup.string(),
value: yup.mixed().when('type', {
is: 'number',
then: yup.number(),
otherwise: yup.string(),
}),
});
Такие схемы обычно создаются один раз и эффективно переиспользуются без дополнительного кеширования.
Yup предоставляет метод clone(), который создаёт новый
экземпляр схемы:
const baseSchema = yup.object({
email: yup.string().email(),
});
const schemaCopy = baseSchema.clone();
Клонирование полезно при необходимости модификации схемы без изменения оригинала, однако оно:
Поэтому clone() следует использовать только при реальной
необходимости изоляции схем.
Yup не предоставляет встроенного механизма кеширования результатов валидации, однако в некоторых сценариях возможно внешнее кеширование.
Пример ключа кеша на основе входных данных:
import * as yup from 'yup';
const schema = yup.object({
email: yup.string().email().required(),
});
const resultCache = new Map();
async function validate(data) {
const key = JSON.stringify(data);
if (resultCache.has(key)) {
return resultCache.get(key);
}
const result = await schema.validate(data);
resultCache.set(key, result);
return result;
}
Такой подход применим только для неизменяемых данных, так как любое изменение входа требует пересчёта ключа.
В UI-слоях схемы часто передаются в компоненты форм. Ошибкой является создание схемы внутри компонента:
function Form() {
const schema = yup.object({
name: yup.string().required(),
});
return null;
}
При каждом рендере создаётся новая схема, что может приводить к:
Правильный подход — использование useMemo:
function Form() {
const schema = useMemo(() => {
return yup.object({
name: yup.string().required(),
});
}, []);
return null;
}
Yup позволяет комбинировать схемы, и здесь кеширование становится особенно важным.
const addressSchema = yup.object({
city: yup.string().required(),
street: yup.string().required(),
});
const userSchema = yup.object({
name: yup.string().required(),
address: addressSchema,
});
Повторное использование addressSchema исключает
необходимость пересоздания вложенной структуры.
В Yup каждая схема содержит цепочку трансформаций и тестов. При сложных формах важно избегать повторного построения одинаковых цепочек.
Оптимизация достигается через:
const requiredString = yup.string().required();
const profileSchema = yup.object({
firstName: requiredString,
lastName: requiredString,
});
В масштабных приложениях схемы обычно организуются как слой конфигурации:
Такая структура позволяет: