Олег Марков
Руководство по валидации форм во Vue.js
Введение
Валидация форм — одно из ключевых требований к современному веб-приложению. Она помогает убедиться, что пользователи отправляют корректные данные, своевременно подсказывает об ошибках и делает интерфейс надёжнее и дружелюбнее. Vue.js предоставляет гибкие инструменты для создания интерактивных форм, однако вопросы валидации здесь требуют внимания к деталям.
В этой статье вы узнаете, как реализовать валидацию форм во Vue.js, освоите встроенные техники, научитесь подключать сторонние библиотеки, а также узнаете о типичных ошибках и лучших практиках. Я подготовил для вас понятные примеры и развернутые объяснения, чтобы вы могли с лёгкостью повторить их в своих проектах.
Типы валидации форм во Vue.js
Прежде чем перейти к практике, кратко поясню — существует два основных подхода к валидации форм:
- Клиентская (на стороне пользователя) — моментальная проверка данных до их отправки;
- Серверная (на сервере, после отправки) — обязательна для финальной безопасности, но не входит в рамки этой статьи.
В этой статье основной фокус будет на клиентской стороне — как превратить обычную Vue-форму в максимально удобную и надёжную.
Ручная реализация валидации на простых формах
Начнем с самой базы. Посмотрите, как можно реализовать базовую валидацию формы руками.
Пример: Валидация "на лету" без библиотек
Допустим, у вас форма с двумя полями: имя и email.
<template>
<form @submit.prevent="submitForm">
<div>
<label>Имя:</label>
<input v-model="form.name" @blur="validateName" />
<span v-if="errors.name" class="error">{{ errors.name }}</span>
</div>
<div>
<label>Email:</label>
<input v-model="form.email" @blur="validateEmail" />
<span v-if="errors.email" class="error">{{ errors.email }}</span>
</div>
<button type="submit">Отправить</button>
</form>
</template>
<script>
export default {
data() {
return {
form: {
name: '',
email: ''
},
errors: {
name: '',
email: ''
}
}
},
methods: {
validateName() {
// Проверяем, что имя введено
if (!this.form.name) {
this.errors.name = 'Имя обязательно';
} else {
this.errors.name = '';
}
},
validateEmail() {
// Простейшая проверка email через регулярку
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!this.form.email) {
this.errors.email = 'Email обязателен';
} else if (!emailPattern.test(this.form.email)) {
this.errors.email = 'Введите корректный Email';
} else {
this.errors.email = '';
}
},
submitForm() {
this.validateName();
this.validateEmail();
// Проверяем, есть ли ошибки
if (!this.errors.name && !this.errors.email) {
// Нет ошибок, отправляем форму
alert('Форма отправлена!');
}
}
}
}
</script>
<style scoped>
.error {
color: red;
font-size: 0.9em;
}
</style>
Здесь вы видите:
- Проверка при событии
blur
(потеря фокуса) - Простые функции проверки каждого поля
- Отображение ошибок под каждым полем
- Блокировка отправки, если есть ошибки
Этот подход отлично подойдёт для небольших проектов, если нужно реализовать специфичную логику или минимальный набор проверок.
Универсализация проверки: динамическая валидация через computed и watch
Иногда хочется, чтобы валидация происходила динамически, без явных вызовов в методах. Для этого удобно использовать вычисляемые свойства и наблюдатели.
Пример: Автоматическая проверка всех полей
<template>
<form @submit.prevent="submitForm">
<div v-for="field in fields" :key="field.name">
<label :for="field.name">{{ field.label }}</label>
<input
:id="field.name"
v-model="form[field.name]"
@blur="() => validateField(field.name)"
/>
<span v-if="errors[field.name]">{{ errors[field.name] }}</span>
</div>
<button :disabled="hasErrors" type="submit">Отправить</button>
</form>
</template>
<script>
export default {
data() {
return {
fields: [
{ name: 'name', label: 'Имя' },
{ name: 'email', label: 'Email' }
],
form: {
name: '',
email: ''
},
errors: {
name: '',
email: ''
}
}
},
computed: {
hasErrors() {
// Проверяем, есть ли хоть одна ошибка
return Object.values(this.errors).some(Boolean);
}
},
methods: {
validateField(field) {
if (field === 'name') {
this.errors.name = this.form.name ? '' : 'Имя обязательно';
}
if (field === 'email') {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!this.form.email) {
this.errors.email = 'Email обязателен';
} else if (!emailPattern.test(this.form.email)) {
this.errors.email = 'Введите корректный Email';
} else {
this.errors.email = '';
}
}
},
submitForm() {
// Валидируем все поля перед отправкой
this.fields.forEach(field => this.validateField(field.name));
if (!this.hasErrors) {
alert('Форма отправлена!');
}
}
},
watch: {
// Автоматически валидируем имя при изменении
'form.name'(val) {
this.validateField('name');
},
// Автоматически валидируем email при изменении
'form.email'(val) {
this.validateField('email');
}
}
}
</script>
В этом примере показана универсальная структура с циклом, computed свойствами и watcher'ами. Как только пользователь вводит что-то новое — ошибки появляются/исчезают автоматически, а кнопка "Отправить" блокируется при наличии ошибок.
Валидация через директиву v-model и кастомные модификаторы
Vue позволяет делать продвинутые вещи через "модификаторы" у директивы v-model, добавляя, например, автоматическую "trim"-обработку или "number"-преобразование входного значения.
Модификаторы, которые могут помочь при валидации
- .trim — автоматически убирает пробелы в начале и конце строки.
- .number — преобразует введенное значение в число.
- .lazy — обновляет модель только на событие change, а не input.
Пример использования
<input v-model.trim="form.name" />
<input v-model.number="form.age" />
<input v-model.lazy="form.email" />
Это не прямая валидация, но такие модификаторы избавляют от мелких ошибок ввода ещё до проверки, что упрощает логику проверки.
Комбинирование ручной валидации и динамических сообщений
Иногда требуется не только статика, но и подсказки — как пользователю заполнять поле. Я покажу, как можно добавить подобные подсказки.
<template>
<div>
<input
v-model="password"
@input="checkPassword"
type="password"
placeholder="Пароль"
/>
<div v-if="passwordHint">{{ passwordHint }}</div>
<div v-if="error" style="color: red">{{ error }}</div>
</div>
</template>
<script>
export default {
data() {
return {
password: '',
error: '',
passwordHint: ''
}
},
methods: {
checkPassword() {
// Подсказка или ошибка по мере ввода
if (this.password.length < 6) {
this.passwordHint = 'Пароль должен быть не меньше 6 символов';
this.error = '';
} else if (!/[A-Z]/.test(this.password)) {
this.passwordHint = 'Добавьте хотя бы одну заглавную букву';
this.error = '';
} else {
this.passwordHint = '';
this.error = '';
}
}
}
}
</script>
Так вы помогаете пользователю ещё до того, как он совершит ошибку.
Использование специализированных библиотек для валидации
Хотя ручная валидация — мощный инструмент, она быстро становится громоздкой по мере усложнения форм. Здесь помогут популярные библиотеки.
Обзор популярных библиотек
Vuelidate
- Легковесна, декларативна.
- Хорошо интегрируется во Vue 2 и Vue 3.
- Позволяет описывать правила как функции/объекты.
- Официальный сайт: https://vuelidate-next.netlify.app/
vee-validate
- Простая декларативная схема.
- Поддерживает сложные сценарии, асинхронную валидацию, кастомные правила.
- Есть готовые компоненты и хелперы.
- Официальный сайт: https://vee-validate.logaretm.com/v4/
Я покажу вам практические примеры обеих библиотек.
Пример: Валидация форм с Vuelidate
Подключить Vuelidate очень просто. Для Vue 3:
npm install @vuelidate/core @vuelidate/validators
Использование на обычной форме
<template>
<form @submit.prevent="submitForm">
<div>
<label>Имя:</label>
<input v-model="form.name" />
<span v-if="!$v.form.name.required">Имя обязательно</span>
</div>
<div>
<label>Email:</label>
<input v-model="form.email" />
<span v-if="!$v.form.email.email">Email некорректен</span>
</div>
<button type="submit" :disabled="$v.$invalid">Отправить</button>
</form>
</template>
<script>
import useVuelidate from '@vuelidate/core'
import { required, email } from '@vuelidate/validators'
export default {
setup() {
const form = reactive({
name: '',
email: ''
})
// Описываем правила проверки
const rules = {
form: {
name: { required },
email: { required, email }
}
}
// Получаем объект $v с состояниями ошибок/валидности
const $v = useVuelidate(rules, { form })
// Функция отправки
function submitForm() {
$v.value.$touch() // Активируем показ ошибок
if (!$v.value.$invalid) {
alert('Форма валидна!')
}
}
return { form, $v, submitForm }
}
}
</script>
Здесь ключевые особенности:
- Правила валидации задаются в виде объектов.
- Состояния ошибок автоматически синхронизируются с компонентом.
$v.form.field
позволяет узнать статус конкретного поля.
Пример: Продвинутая валидация с vee-validate
Еще один вариант — vee-validate. Установите пакет:
npm install vee-validate yup
Использование с Yup (схема проверки)
<template>
<Form @submit="onSubmit" :validation-schema="schema">
<Field name="name" />
<ErrorMessage name="name" />
<Field name="email" />
<ErrorMessage name="email" />
<button type="submit">Отправить</button>
</Form>
</template>
<script>
import { Form, Field, ErrorMessage } from 'vee-validate'
import * as yup from 'yup'
export default {
components: { Form, Field, ErrorMessage },
setup() {
// Описываем схему проверки через yup
const schema = yup.object({
name: yup.string().required('Имя обязательно'),
email: yup.string().email('Email некорректен').required('Email обязателен')
})
// Обработчик отправки
function onSubmit(values) {
// values — уже валидированные данные
alert(JSON.stringify(values))
}
return { schema, onSubmit }
}
}
</script>
Плюсы подхода:
- Разделение логики формы и самой валидации.
- Автоматическое отображение ошибок.
- Возможность повторного использования схем валидации.
Кастомные правила валидации
Библиотеки позволяют легко добавлять собственные правила.
Пример: Кастомное правило в Vuelidate
import { helpers } from '@vuelidate/validators'
// Простое кастомное правило
const mustContainNumber = helpers.withMessage(
'Пароль должен содержать хотя бы одну цифру',
value => /\d/.test(value)
)
Пример: Кастомное правило в vee-validate
import { defineRule } from 'vee-validate'
defineRule('noSpaces', value => {
if (/\s/.test(value)) {
return 'Пробелы не допускаются'
}
return true
})
После этого вы используете правило в Field, как и встроенное.
Асинхронная валидация (например, проверка занятости e-mail)
Бывает нужно, чтобы значение проходило проверку на сервере (например, email уже занят).
Пример: Асинхронная проверка в Vuelidate
import { helpers } from '@vuelidate/validators'
const asyncEmailCheck = helpers.withAsync(async value => {
// Здесь обычно идет fetch запрос
const response = await fetch(`/api/check-email?email=${value}`)
const { exists } = await response.json()
return !exists // true, если email свободен
})
Особенности
- Асинхронная проверка может занимать время.
- Важно показывать пользователю индикатор проверки.
- После результата — показывать ошибку или успех.
Общие best practices при валидации форм во Vue.js
- Разделяйте валидацию и UI-логику. Держите правила и шаблоны отдельно для читаемости.
- Показывайте ошибки как можно раньше. Либо по вводу, либо на blur, но не только после отправки.
- Добавляйте подсказки по формату: Даже валидный email тяжело "угадывать" по ошибке, дайте пример.
- Используйте сторонние библиотеки, если форма большая или условия часто повторяются.
- Асинхронные проверки всегда делайте с debounce (например через lodash.debounce) — чтобы не нагружать сервер на каждый символ.
- Старайтесь не дублировать логику на фронте и бэке: На клиенте просто удобство, основная проверка всё равно должна быть на сервере.
Заключение
Валидация форм во Vue.js — базовый и обязательный навык для любого разработчика, работающего с пользовательскими данными. Вы можете обойтись ручной проверкой простых форм, но для расширяемости, повторного использования и поддержки стоит подключать специализированные библиотеки, такие как Vuelidate или vee-validate.
Важный момент — не смешивать валидацию и отображение, стараться использовать декларативный подход и не забывать о серверной валидации. Динамические сообщения и подсказки значительно улучшают пользовательский опыт.
Частозадаваемые технические вопросы по теме
Как очистить ошибки при сбросе/очистке формы?
Сбросьте значения ошибок в data или state, а также вызовите reset-методы, если используете библиотеку. Пример для Vuelidate:
// Очищаем ошибки и поля
this.form.name = ''
this.form.email = ''
this.$v.$reset()
Для vee-validate достаточно вызвать resetForm
.
Как реализовать валидацию для вложенных объектов и массивов?
В Vuelidate можно описывать правила для вложенных структур через вложенные объекты в схеме правил. В vee-validate используйте yup.object или yup.array для описания вложенных данных.
Как остановить показ ошибок до первой попытки отправки?
Используйте флаг, например, wasSubmitted
, и не отображайте сообщения, пока он не станет true. В большинстве библиотек есть поддержка touched/dirty-состояний, используйте их для подобного же эффекта.
Как поддерживать динамические поля (например, список телефонов)?
В vee-validate можно создать массив полей через yup.array и динамически добавлять компоненты Field. В Vuelidate используйте массивы в структуре данных и схемах, итерируйте ошибки по индексам.
Как валидировать формы на TypeScript во Vue 3?
Обе библиотеки поддерживают TypeScript. Для Vuelidate используйте типизацию схем объектов и значение useVuelidate. Для vee-validate лучше всего подходит связка с yup, где вы явно описываете схемы и используете типы yup для form values.