Валидация форм — повторяющаяся и трудоёмкая задача. **VeeValidate** — наиболее популярная библиотека валидации для Vue 3. Она предоставляет два подхода: компонентный (через <Field> и <Form>) и composable-API. Современный подход — через Composition API.
npm install vee-validate
# Опционально — схемы валидации:
npm install yup
# или
npm install zodimport { useForm, useField } from 'vee-validate'
const { handleSubmit, errors, isSubmitting } = useForm({
validationSchema: {
email: (val) => {
if (!val) return 'Email обязателен'
if (!/^[^@]+@[^@]+.[^@]+$/.test(val)) return 'Некорректный email'
return true // валидация прошла
},
password: (val) => {
if (!val) return 'Пароль обязателен'
if (val.length < 8) return 'Минимум 8 символов'
return true
},
},
})
const { value: email, errorMessage: emailError } = useField('email')
const { value: password, errorMessage: passwordError } = useField('password')
const onSubmit = handleSubmit((values) => {
console.log('Форма валидна:', values)
// Отправляем данные
})<template>
<form @submit="onSubmit">
<div>
<input v-model="email" type="email" placeholder="Email" />
<span class="error">{{ emailError }}</span>
</div>
<div>
<input v-model="password" type="password" placeholder="Пароль" />
<span class="error">{{ passwordError }}</span>
</div>
<button :disabled="isSubmitting" type="submit">
{{ isSubmitting ? 'Загрузка...' : 'Войти' }}
</button>
</form>
</template>**Yup** позволяет описывать схемы валидации декларативно:
import { useForm } from 'vee-validate'
import * as yup from 'yup'
const schema = yup.object({
name: yup.string().required('Имя обязательно').min(2, 'Минимум 2 символа'),
email: yup.string().required('Email обязателен').email('Некорректный email'),
age: yup.number().min(18, 'Минимальный возраст 18 лет').max(120),
password: yup.string()
.required('Пароль обязателен')
.min(8, 'Минимум 8 символов')
.matches(/[A-Z]/, 'Нужна хотя бы одна заглавная буква'),
confirmPassword: yup.string()
.oneOf([yup.ref('password')], 'Пароли не совпадают'),
})
const { handleSubmit, errors } = useForm({ validationSchema: schema })import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'
const schema = toTypedSchema(z.object({
email: z.string().email('Некорректный email'),
password: z.string().min(8, 'Минимум 8 символов'),
}))
const { handleSubmit } = useForm({ validationSchema: schema })import { defineRule, configure } from 'vee-validate'
import { required, email, min } from '@vee-validate/rules'
// Регистрируем глобальные правила
defineRule('required', required)
defineRule('email', email)
defineRule('min', min)
// Настраиваем сообщения об ошибках
configure({
generateMessage: (context) => {
const messages = {
required: `Поле "${context.field}" обязательно`,
email: `Некорректный email`,
min: `Минимум ${context.rule.params[0]} символов`,
}
return messages[context.rule.name] || 'Недопустимое значение'
},
})const {
handleSubmit,
errors, // { field: 'сообщение об ошибке' }
values, // текущие значения всех полей
resetForm, // очистить форму и ошибки
setFieldValue, // программно установить значение
setErrors, // установить серверные ошибки
isSubmitting, // true пока выполняется handleSubmit
meta, // { valid, dirty, touched, pending }
} = useForm()
// Серверные ошибки (например, 'email уже занят')
setErrors({ email: 'Этот email уже зарегистрирован' })Реализация движка валидации форм с поддержкой правил, схем и сообщений об ошибках — аналог ядра VeeValidate
// ============================================
// Движок валидации форм
// (упрощённая модель VeeValidate)
// ============================================
// Встроенные правила валидации
const rules = {
required: (value) => {
if (value === null || value === undefined || value === '') {
return 'Это поле обязательно'
}
return true
},
email: (value) => {
if (!value) return true // required обрабатывает пустоту
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return re.test(value) ? true : 'Некорректный email'
},
min: (value, [minLength]) => {
if (!value) return true
return String(value).length >= Number(minLength)
? true
: `Минимум ${minLength} символов`
},
max: (value, [maxLength]) => {
if (!value) return true
return String(value).length <= Number(maxLength)
? true
: `Максимум ${maxLength} символов`
},
matches: (value, [pattern, message]) => {
if (!value) return true
const re = new RegExp(pattern)
return re.test(value) ? true : (message || 'Некорректный формат')
},
minValue: (value, [min]) => {
if (value === '' || value === null) return true
return Number(value) >= Number(min)
? true
: `Минимальное значение: ${min}`
},
}
// ============================================
// Валидация одного поля
// ============================================
function validateField(value, fieldRules) {
if (!fieldRules) return null
// fieldRules может быть функцией или массивом правил
if (typeof fieldRules === 'function') {
const result = fieldRules(value)
return result === true ? null : result
}
// Обрабатываем массив правил: ['required', 'email', ['min', 8]]
for (const rule of fieldRules) {
let ruleName, params
if (Array.isArray(rule)) {
[ruleName, ...params] = rule
} else {
ruleName = rule
params = []
}
const validator = rules[ruleName]
if (!validator) {
console.warn(`Правило не найдено: "${ruleName}"`)
continue
}
const result = validator(value, params)
if (result !== true) return result // первая ошибка
}
return null // валидация прошла
}
// ============================================
// Класс формы (аналог useForm)
// ============================================
class Form {
constructor(schema) {
this.schema = schema
this.values = {}
this.errors = {}
this.touched = {}
// Инициализируем значения по умолчанию
for (const field of Object.keys(schema)) {
this.values[field] = ''
this.errors[field] = null
this.touched[field] = false
}
}
setValue(field, value) {
this.values[field] = value
this.touched[field] = true
// Валидируем поле при изменении
this.errors[field] = validateField(value, this.schema[field])
}
validateAll() {
let isValid = true
for (const [field, rules] of Object.entries(this.schema)) {
this.errors[field] = validateField(this.values[field], rules)
this.touched[field] = true
if (this.errors[field]) isValid = false
}
return isValid
}
submit(onSuccess) {
const isValid = this.validateAll()
if (isValid) {
onSuccess({ ...this.values })
} else {
console.log(' Форма содержит ошибки, отправка отменена')
}
return isValid
}
reset() {
for (const field of Object.keys(this.schema)) {
this.values[field] = ''
this.errors[field] = null
this.touched[field] = false
}
}
get isValid() {
return Object.values(this.errors).every(e => e === null)
}
}
// ============================================
// Демонстрация
// ============================================
console.log('=== Форма регистрации ===')
const form = new Form({
name: ['required', ['min', 2], ['max', 50]],
email: ['required', 'email'],
password: [
'required',
['min', 8],
['matches', '[A-Z]', 'Нужна хотя бы одна заглавная буква'],
],
age: ['required', ['minValue', 18]],
})
// Симуляция пользовательского ввода
console.log('\nПользователь вводит данные:')
form.setValue('name', 'A') // слишком короткое
console.log('name error:', form.errors.name)
form.setValue('name', 'Alice')
console.log('name error:', form.errors.name) // null
form.setValue('email', 'не-email')
console.log('email error:', form.errors.email)
form.setValue('email', 'alice@example.com')
console.log('email error:', form.errors.email) // null
form.setValue('password', 'weak')
console.log('password error:', form.errors.password)
form.setValue('password', 'StrongPass1')
console.log('password error:', form.errors.password) // null
form.setValue('age', '15')
console.log('age error:', form.errors.age)
form.setValue('age', '25')
console.log('age error:', form.errors.age) // null
console.log('\nОтправка формы:')
form.submit((values) => {
console.log('Форма отправлена!', values)
})
console.log('\nПопытка отправить с ошибкой:')
form.setValue('email', 'bad-email')
form.submit((values) => {
console.log('Это не должно вывестись')
})
console.log('Ошибки:', form.errors)Валидация форм — повторяющаяся и трудоёмкая задача. **VeeValidate** — наиболее популярная библиотека валидации для Vue 3. Она предоставляет два подхода: компонентный (через <Field> и <Form>) и composable-API. Современный подход — через Composition API.
npm install vee-validate
# Опционально — схемы валидации:
npm install yup
# или
npm install zodimport { useForm, useField } from 'vee-validate'
const { handleSubmit, errors, isSubmitting } = useForm({
validationSchema: {
email: (val) => {
if (!val) return 'Email обязателен'
if (!/^[^@]+@[^@]+.[^@]+$/.test(val)) return 'Некорректный email'
return true // валидация прошла
},
password: (val) => {
if (!val) return 'Пароль обязателен'
if (val.length < 8) return 'Минимум 8 символов'
return true
},
},
})
const { value: email, errorMessage: emailError } = useField('email')
const { value: password, errorMessage: passwordError } = useField('password')
const onSubmit = handleSubmit((values) => {
console.log('Форма валидна:', values)
// Отправляем данные
})<template>
<form @submit="onSubmit">
<div>
<input v-model="email" type="email" placeholder="Email" />
<span class="error">{{ emailError }}</span>
</div>
<div>
<input v-model="password" type="password" placeholder="Пароль" />
<span class="error">{{ passwordError }}</span>
</div>
<button :disabled="isSubmitting" type="submit">
{{ isSubmitting ? 'Загрузка...' : 'Войти' }}
</button>
</form>
</template>**Yup** позволяет описывать схемы валидации декларативно:
import { useForm } from 'vee-validate'
import * as yup from 'yup'
const schema = yup.object({
name: yup.string().required('Имя обязательно').min(2, 'Минимум 2 символа'),
email: yup.string().required('Email обязателен').email('Некорректный email'),
age: yup.number().min(18, 'Минимальный возраст 18 лет').max(120),
password: yup.string()
.required('Пароль обязателен')
.min(8, 'Минимум 8 символов')
.matches(/[A-Z]/, 'Нужна хотя бы одна заглавная буква'),
confirmPassword: yup.string()
.oneOf([yup.ref('password')], 'Пароли не совпадают'),
})
const { handleSubmit, errors } = useForm({ validationSchema: schema })import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'
const schema = toTypedSchema(z.object({
email: z.string().email('Некорректный email'),
password: z.string().min(8, 'Минимум 8 символов'),
}))
const { handleSubmit } = useForm({ validationSchema: schema })import { defineRule, configure } from 'vee-validate'
import { required, email, min } from '@vee-validate/rules'
// Регистрируем глобальные правила
defineRule('required', required)
defineRule('email', email)
defineRule('min', min)
// Настраиваем сообщения об ошибках
configure({
generateMessage: (context) => {
const messages = {
required: `Поле "${context.field}" обязательно`,
email: `Некорректный email`,
min: `Минимум ${context.rule.params[0]} символов`,
}
return messages[context.rule.name] || 'Недопустимое значение'
},
})const {
handleSubmit,
errors, // { field: 'сообщение об ошибке' }
values, // текущие значения всех полей
resetForm, // очистить форму и ошибки
setFieldValue, // программно установить значение
setErrors, // установить серверные ошибки
isSubmitting, // true пока выполняется handleSubmit
meta, // { valid, dirty, touched, pending }
} = useForm()
// Серверные ошибки (например, 'email уже занят')
setErrors({ email: 'Этот email уже зарегистрирован' })Реализация движка валидации форм с поддержкой правил, схем и сообщений об ошибках — аналог ядра VeeValidate
// ============================================
// Движок валидации форм
// (упрощённая модель VeeValidate)
// ============================================
// Встроенные правила валидации
const rules = {
required: (value) => {
if (value === null || value === undefined || value === '') {
return 'Это поле обязательно'
}
return true
},
email: (value) => {
if (!value) return true // required обрабатывает пустоту
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return re.test(value) ? true : 'Некорректный email'
},
min: (value, [minLength]) => {
if (!value) return true
return String(value).length >= Number(minLength)
? true
: `Минимум ${minLength} символов`
},
max: (value, [maxLength]) => {
if (!value) return true
return String(value).length <= Number(maxLength)
? true
: `Максимум ${maxLength} символов`
},
matches: (value, [pattern, message]) => {
if (!value) return true
const re = new RegExp(pattern)
return re.test(value) ? true : (message || 'Некорректный формат')
},
minValue: (value, [min]) => {
if (value === '' || value === null) return true
return Number(value) >= Number(min)
? true
: `Минимальное значение: ${min}`
},
}
// ============================================
// Валидация одного поля
// ============================================
function validateField(value, fieldRules) {
if (!fieldRules) return null
// fieldRules может быть функцией или массивом правил
if (typeof fieldRules === 'function') {
const result = fieldRules(value)
return result === true ? null : result
}
// Обрабатываем массив правил: ['required', 'email', ['min', 8]]
for (const rule of fieldRules) {
let ruleName, params
if (Array.isArray(rule)) {
[ruleName, ...params] = rule
} else {
ruleName = rule
params = []
}
const validator = rules[ruleName]
if (!validator) {
console.warn(`Правило не найдено: "${ruleName}"`)
continue
}
const result = validator(value, params)
if (result !== true) return result // первая ошибка
}
return null // валидация прошла
}
// ============================================
// Класс формы (аналог useForm)
// ============================================
class Form {
constructor(schema) {
this.schema = schema
this.values = {}
this.errors = {}
this.touched = {}
// Инициализируем значения по умолчанию
for (const field of Object.keys(schema)) {
this.values[field] = ''
this.errors[field] = null
this.touched[field] = false
}
}
setValue(field, value) {
this.values[field] = value
this.touched[field] = true
// Валидируем поле при изменении
this.errors[field] = validateField(value, this.schema[field])
}
validateAll() {
let isValid = true
for (const [field, rules] of Object.entries(this.schema)) {
this.errors[field] = validateField(this.values[field], rules)
this.touched[field] = true
if (this.errors[field]) isValid = false
}
return isValid
}
submit(onSuccess) {
const isValid = this.validateAll()
if (isValid) {
onSuccess({ ...this.values })
} else {
console.log(' Форма содержит ошибки, отправка отменена')
}
return isValid
}
reset() {
for (const field of Object.keys(this.schema)) {
this.values[field] = ''
this.errors[field] = null
this.touched[field] = false
}
}
get isValid() {
return Object.values(this.errors).every(e => e === null)
}
}
// ============================================
// Демонстрация
// ============================================
console.log('=== Форма регистрации ===')
const form = new Form({
name: ['required', ['min', 2], ['max', 50]],
email: ['required', 'email'],
password: [
'required',
['min', 8],
['matches', '[A-Z]', 'Нужна хотя бы одна заглавная буква'],
],
age: ['required', ['minValue', 18]],
})
// Симуляция пользовательского ввода
console.log('\nПользователь вводит данные:')
form.setValue('name', 'A') // слишком короткое
console.log('name error:', form.errors.name)
form.setValue('name', 'Alice')
console.log('name error:', form.errors.name) // null
form.setValue('email', 'не-email')
console.log('email error:', form.errors.email)
form.setValue('email', 'alice@example.com')
console.log('email error:', form.errors.email) // null
form.setValue('password', 'weak')
console.log('password error:', form.errors.password)
form.setValue('password', 'StrongPass1')
console.log('password error:', form.errors.password) // null
form.setValue('age', '15')
console.log('age error:', form.errors.age)
form.setValue('age', '25')
console.log('age error:', form.errors.age) // null
console.log('\nОтправка формы:')
form.submit((values) => {
console.log('Форма отправлена!', values)
})
console.log('\nПопытка отправить с ошибкой:')
form.setValue('email', 'bad-email')
form.submit((values) => {
console.log('Это не должно вывестись')
})
console.log('Ошибки:', form.errors)Напиши функцию `createValidator(schema)`, которая принимает объект схемы (где каждое значение — функция-валидатор, возвращающая строку с ошибкой или `null`) и возвращает функцию `validate(data)`. Функция `validate` должна: пройти по всем полям схемы, вызвать соответствующий валидатор, и вернуть объект `{ isValid: boolean, errors: { [field]: string | null } }`.
В validate(data): создай объект errors = {}, переменную isValid = true. Для каждого поля в schema: const error = schema[field](data[field]); errors[field] = error; if (error !== null) isValid = false. Верни { isValid, errors }.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке