La integridad de los datos es fundamental en el desarrollo frontend moderno. En plantillas basadas en Vite y Vue, como Vitesse, estructurar la validación de formularios de manera eficiente es crucial para mantener la escalabilidad del código y ofrecer una experiencia de usuario fluida.
Requisitos Fundamentales de un Sistema de Validación
Un sistema robusto de validación debe cumplir con los siguientes pilares arquitectónicos:
- Inmediatez: Retroalimentación visual y programática mientras el usuario interactúa con los campos.
- Precisión: Cumplimiento estricto de los esquemas de datos y tipos esperados.
- Claridad: Mensajes de error explícitos, localizados y contextualizados.
- Modularidad: Capacidad de reutilizar reglas de negocio entre distintos componentes y vistas.
Estrategias de Implementación en Vue
1. Lógica Embebida en el Componente
Para formularios sencillos o prototipos rápidos, la validación puede residir directamente en el estado del componente. A continuación, se muestra un enfoque utilizando el Composition API para validar edad y una clave de acceso:
<script setup>
import { reactive } from 'vue'
const formData = reactive({
userAge: null,
secretKey: ''
})
const validationState = reactive({
ageMsg: '',
keyMsg: ''
})
const checkInputs = () => {
let hasErrors = false
if (!formData.userAge || formData.userAge < 18) {
validationState.ageMsg = 'La edad mínima requerida es 18 años.'
hasErrors = true
} else {
validationState.ageMsg = ''
}
if (formData.secretKey.length < 8) {
validationState.keyMsg = 'La clave secreta debe tener al menos 8 caracteres.'
hasErrors = true
} else {
validationState.keyMsg = ''
}
return !hasErrors
}
</script>
2. Abstracción mediante Composables
Para evitar la duplicación de código, es recomendalbe extraer la lógica de validación a un composable reutilizable. Esto mantiene los componentes limpios y centraliza las reglas:
// src/composables/useFormChecks.ts
import { ref } from 'vue'
export function useFormChecks() {
const fieldErrors = ref<Record<string, string>>({})
const runValidation = (
fieldName: string,
value: any,
rule: (val: any) => boolean,
errorMsg: string
) => {
if (!rule(value)) {
fieldErrors.value[fieldName] = errorMsg
return false
}
delete fieldErrors.value[fieldName]
return true
}
return {
fieldErrors,
runValidation
}
}
Integración del composable en la vista:
<script setup lang="ts">
import { useFormChecks } from '@/composables/useFormChecks'
const { fieldErrors, runValidation } = useFormChecks()
const processSubmission = (payload: any) => {
const isAgeOk = runValidation('userAge', payload.userAge, (v) => v >= 18, 'Edad inválida')
const isKeyOk = runValidation('secretKey', payload.secretKey, (v) => v.length >= 8, 'Clave muy corta')
if (isAgeOk && isKeyOk) {
// Ejecutar llamada a la API
}
}
</script>
3. Integración de Librerías Especializadas (VeeValidate + Zod)
En aplicaciones empresariales con esquemas complejos, delegar la validaicón a librerías especializadas es la práctica estándar. La combinación de VeeValidate con Zod ofrece inferenica de tipos nativa y un rendimiento excepcional en entornos Vite.
Definición del esquema de datos:
// src/schemas/userSchema.ts
import { z } from 'zod'
export const userProfileSchema = z.object({
username: z.string().min(3, 'El nombre de usuario es muy corto').max(20, 'Máximo 20 caracteres'),
contactEmail: z.string().email('Formato de correo electrónico inválido'),
accountType: z.enum(['free', 'premium', 'enterprise'], {
errorMap: () => ({ message: 'Seleccione un tipo de cuenta válido' })
})
})
Configuración del formulario en el componente:
<script setup lang="ts">
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { userProfileSchema } from '@/schemas/userSchema'
const validationSchema = toTypedSchema(userProfileSchema)
const { handleSubmit, defineField, errors } = useForm({
validationSchema,
})
const [username, usernameAttrs] = defineField('username')
const [contactEmail, emailAttrs] = defineField('contactEmail')
const onSubmit = handleSubmit((values) => {
console.log('Datos válidos:', values)
// Llamada a la API
})
</script>
Prácticas Recomendadas para la Experiencia de Usuario y Rendimiento
- Gestión de Eventos y Debounce: Para validaciones en tiempo real (como verificar disponibilidad de un nombre de usuario), implemente funciones de debounce para evitar saturar la red o el hilo principal con cada pulsación de tecla.
- Arquitectura de Mensajes de Error: Posicione las alertas de error inmediatamente debajo del campo correspondiente. Utilice atributos ARIA como
aria-invalidyaria-describedbypara garantizar que los lectores de pantalla anuncien los errores a usuarios con discapacidad visual. - Optimización de Costo Computacional: Evite ejecutar expresiones regulares complejas o consultas a bases de datos en el evento
@input. Reserve las validaciones asíncronas pesadas para el evento@bluro justo antes del envío del formulario. - Validación por Pasos (Wizards): En formularios extensos, divida la lógica en pasos independientes. Valide el estado actual antes de permitir la transición al siguiente paso, reduciendo la carga cognitiva del usuario.