логотип PurpleSchool
логотип PurpleSchool

Руководство по валидации форм во 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.

Интеграция Tiptap для создания редакторов на VueСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеОбработка ошибок и отладка в Vue.jsНастройка ESLint для Vue проектов и поддержка качества кодаИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueВзаимодействие с внешними API через Axios в VueКогда и как использовать $emit и call во Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийИспользование Docker для контейнеризации приложений на VueРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний