Vue.js: النماذج و v-model
آخر تحديث: 2026-10-20
تُعد النماذج جوهر التفاعل في تطبيقات الويب — فعمليات تسجيل الدخول والتسجيل والبحث والإعدادات تعتمد جميعها على معالجة النماذج. ويُعد v-model في Vue بمثابة «تسهيل لغوي» لمعالجة النماذج، مما يجعل كود الربط ثنائي الاتجاه موجزًا للغاية (سطر واحد مقابل 5 أسطر).
يعمل defineModel في Vue 3.4+ على تبسيط تنفيذ v-model بشكل أكبر. كما تتطلب النماذج المعقدة ميزات متقدمة مثل التحقق من الصحة، والتسلسل، وتحميل الملفات. يتناول هذا الدرس خمسة سيناريوهات رئيسية للنماذج.
1. ما ستتعلمه
- كيف يعمل نموذج V (بشكل أساسي، هو عبارة عن «props + emit» في صيغة مبسطة)
- مكونات نموذج V المخصصة
- تبسيط defineModel (Vue 3.4+)
- التحقق من صحة النماذج (VeeValidate / مخصص)
- تصميم نماذج معقدة (حقول متداخلة / ديناميكية)
- تسلسل النماذج وإرسالها
- تحميل الملفات ومعاينتها
2. كابوس «الأقسام النمطية الخمسة» في نموذج تسجيل الدخول
(1) المشكلة: 5 حقول في النموذج، و10 أسطر من كود v-model
كان نموذج تسجيل الدخول الخاص بأليس يتألف من 5 حقول، كل منها مرتبط بنموذج v-model:
5 حقول = 10 أسطر من النص المعياري. 10 حقول تعادل 20 سطراً. وكلما زادت تعقيد الاستمارة، زادت المشكلة سوءًا.
(2) نهج متقدم لاستخدام v-model في Vue: الكائنات التفاعلية + defineModel
5 حقول = كائن واحد. تقلل البرمجة التفاعلية حجم الكود بنسبة 50%.
(3) الإيرادات
بعد تحسين نموذج V:
- حجم الكود: 10 أسطر → 5 أسطر (-50٪)
- الحقول الجديدة: صف واحد (تمت إضافة حقول تفاعلية)
- إرسال النموذج بالكامل: ما عليك سوى إرسال كائن النموذج
- التحقق من الصحة: المعالجة المركزية
3. كيف يعمل نموذج V
(1) جوهر نموذج V
<!-- v-model is props + emit syntax sugar -->
<input v-model="searchQuery">
<!-- equivalent to -->
<input
:value="searchQuery"
@input="searchQuery = $event.target.value"
>
(2) نموذج V في المكونات
<!-- Using Parent Components v-model -->
<CustomInput v-model="searchQuery" />
<!-- equivalent to -->
<CustomInput
:modelValue="searchQuery"
@update:modelValue="searchQuery = $evento"
/>
(3) defineModel (مبسطة لـ Vue 3.4 وما بعدها)
▶ مثال: 5 أساسيات نموذج V
<!-- 1. text input -->
<input v-model="text">
<!-- 2. textarea -->
<textarea v-model="description"></textarea>
<!-- 3. checkbox -->
<input v-model="agreed" type="checkbox">
<!-- 4. radio -->
<input v-model="gender" type="radio" value="male">
<input v-model="gender" type="radio" value="female">
<!-- 5. select -->
<select v-model="selected">
<option value="vue">Vue</option>
<option value="react">React</option>
</select>
الإخراج:
البداية: الحقول النصية فارغة وagreed غير محددة وgender وselected بلا قيمة
الكتابة أو الاختيار تحدّث المراجع فورًا: gender = male، وselected = vue
4. 5 أنواع من نموذج V المخصص
(1) المدخلات الأساسية
(2) الرسائل النصية
(3) حدد
(4) مربع الاختيار (اختيارات متعددة)
(5) مُعدِّل مخصص (trim)
<!-- Parent Component:v-model.trim -->
<CustomInput v-model.trim="username" />
<!-- Child component:Processing trim Modifiers -->
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
function onInput(e) {
let value = e.target.value
if (props.modelModifiers.trim) {
value = value.trim()
}
emit('update:modelValue', value)
}
</script>
▶ مثال: 5 من أهم عوامل تعديل الصيغة
<!-- 1. .lazy:change Event -->
<input v-model.lazy="email">
<!-- 2. .number:Automatically Convert to Numbers -->
<input v-model.number="age" type="number">
<!-- 3. .trim:Automatic trim -->
<input v-model.trim="username">
<!-- 4. Combinations of Multiple Modifiers -->
<input v-model.lazy.trim="email">
<!-- 5. Custom Modifiers(Child component)-->
<CustomInput v-model.trim="text" />
الإخراج:
lazy: لا تتحدث القيمة إلا عند change (فقدان التركيز)
trim: تُحذف المسافات الطرفية قبل الحفظ، وnumber: تتحول القيمة إلى رقم
lazy.trim معًا: التحديث عند change مع قص المسافات
5. التحقق من صحة البيانات في النموذج
(1) التحقق من الصحة المخصص
// utils/validators.js
export function isEmail(value) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
}
export function isPhone(value) {
return /^1[3-9]\d{9}$/.test(value)
}
export function minLength(value, min) {
return value.length >= min
}
<template>
<form @submit.prevent="handleSubmit">
<input v-model="form.email" @blur="validateField('email')">
<p v-if="errors.email" class="error">{{ errors.email }}</p>
<input v-model="form.password" type="password">
<p v-if="errors.password">{{ errors.password }}</p>
<button :disabled="!isValid">Submit</button>
</form>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { isEmail, minLength } from '@/utils/validators'
const form = reactive({
email: '',
password: ''
})
const errors = reactive({})
const isValid = ref(false)
function validateField(field) {
if (field === 'email') {
if (!form.email) {
errors.email = 'Email is required'
} else if (!isEmail(form.email)) {
errors.email = 'Invalid email format'
} else {
delete errors.email
}
}
if (field === 'password') {
if (!form.password) {
errors.password = 'Password is required'
} else if (!minLength(form.password, 8)) {
errors.password = 'Password must be 8+ characters'
} else {
delete errors.password
}
}
isValid.value = Object.keys(errors).length === 0
}
function handleSubmit() {
Object.keys(form).forEach(validateField)
if (isValid.value) {
console.log('Submit:', form)
}
}
</script>
(2) مكتبة VeeValidate (موصى بها)
npm install vee-validate @vee-validate/rules
<template>
<Form @submit="handleSubmit" :validation-schema="schema">
<Field name="email" type="email" v-model="form.email" />
<ErrorMessage name="email" />
<Field name="password" type="password" v-model="form.password" />
<ErrorMessage name="password" />
<button>Submit</button>
</Form>
</template>
<script setup>
import { Form, Field, ErrorMessage } from 'vee-validate'
import * as yup from 'yup'
const schema = yup.object({
email: yup.string().email().required(),
password: yup.string().min(8).required()
})
const form = reactive({ email: '', password: '' })
function handleSubmit(values) {
console.log('Valid:', values)
}
</script>
▶ مثال: 5 سيناريوهات التحقق من الصحة
// 1. Required
if (!form.email) errors.email = 'Required'
// 2. Email
if (!isEmail(form.email)) errors.email = 'Invalid email'
// 3. Cell Phone
if (!isPhone(form.phone)) errors.phone = 'Invalid phone'
// 4. Length
if (form.password.length < 8) errors.password = 'Too short'
// 5. Confirm Password
if (form.password !== form.confirm) errors.confirm = 'Mismatch'
الإخراج:
نموذج فارغ → errors.email = Required وerrors.password = Too short
بريد غير صالح → Invalid email، وكلمتا مرور مختلفتان → Mismatch
6. تصميم النماذج المعقدة
(1) النماذج المتداخلة
const form = reactive({
user: {
name: '',
email: '',
age: 0
},
address: {
city: '',
country: ''
}
})
(2) الحقول الديناميكية (v-for)
const form = reactive({
items: [
{ name: '', quantity: 1, price: 0 }
]
})
function addItem() {
form.items.push({ name: '', quantity: 1, price: 0 })
}
function removeItem(index) {
form.items.splice(index, 1)
}
(3) المُعدِّلات الخمسة الرئيسية للشكل
<!-- 1. .lazy:change Event Trigger(No input) -->
<input v-model.lazy="email">
<!-- 2. .number:Automatically Convert to Numbers -->
<input v-model.number="age" type="number">
<!-- 3. .trim:Automatically Remove Spaces -->
<input v-model.trim="username">
<!-- 4. Custom Modifiers(CustomInput) -->
<CustomInput v-model.trim="text" />
<!-- 5. .debounce(Custom):300ms Image Stabilization -->
<DebouncedInput v-model:debounce="500" />
7. تحميل الملفات
(1) تحميل ملف واحد
(2) ملفات متعددة + السحب والإفلات
8. تمرين شامل: v-model مخصّص بين المكوّنين
▶ مثال: 5 أنواع من نماذج V المخصصة
▶ جرّب الكودالإخراج:
الكتابة في CustomInput تنعكس على المتغير search في الأب مباشرة
السطر المكافئ :modelValue مع @update:modelValue يوضح أن v-model اختصار للاثنين
(1) 📊 5. مرجع سريع لـ 5 أخطاء شائعة
| الخطأ | الأعراض | الحل |
|---|---|---|
| v-model.number لا يزال سلسلة نصية | خطأ في الجمع | type="number" + .number |
| لا يتم تحديث نموذج v | تصبح المرجعية غير نشطة بعد التغيير إلى النمط التفاعلي | قم بالتغيير إلى .value أو toRefs |
| فشل تحميل الملف باستخدام FormData | خطأ متعلق بالنطاقات المتعددة أو بالطريقة | fetch + FormData |
| توقيت التحقق غير صحيح | العرض فقط عند فقدان التركيز | استخدام @blur |
| الكثير من النصوص النمطية الميدانية | كود مطول | باستخدام كائنات تفاعلية |
(2) 📊 6. 5 مقارنات رئيسية في الأداء
| النمط | البساطة | الأداء | قابلية التطبيق |
|---|---|---|---|
| مراجع متعددة | ❌ مكرر | ⭐⭐⭐⭐ | حقلان أو حقل واحد |
| كائن تفاعلي | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 5 حقول أو أكثر |
| FormData (دليل) | ❌ | ⭐⭐⭐ | تحميل الملفات |
| VeeValidate | ⭐⭐⭐⭐ | ⭐⭐⭐ | التحقق المعقد |
| defineModel | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | مكون Vue 3.4+ |
❓ أسئلة شائعة
defineProps(['modelValue']) + defineEmits(['update:modelValue']) داخل المكون. واستخدم v-model في المكون الأصلي. في Vue 3.4 وما فوق، استخدم defineModel لاستبدال 10 أسطر بسطر واحد فقط.type="number". أو يمكنك استخدام parseFloat() يدويًّا.FileReader.readAsDataURL() لقراءة الملف كسلسلة Base64، ثم قم بتعيينه إلى <img src="...">. Use FormData من أجل التحميل.change (بعد فقدان التركيز)، مما يقلل من العبء الناتج عن التحقق من الصحة في الوقت الفعلي. أما المُعدِّل .debounce فهو مُعدِّل مخصص يجب تنفيذه في المكون الفرعي؛ وهو يقوم بتشغيل آخر حدث في غضون 300 مللي ثانية.JSON.stringify(form) (لا حاجة إلى FormData، ما لم تكن ترسل ملفات). ويمكن للخادم الخلفي استلام ملف JSON ببساطة.defineModel محل الصيغة الكاملة لـ v-model؟defineModel('modelValue') is equivalent to the 10 lines of code in props.modelValue + emit('update:modelValue'). وهو متوافق تمامًا مع الإصدارات السابقة.📖 ملخص
v-modelهو تعبير مبسط لـpropsوemit؛ وفي الإصدار Vue 3.4 والإصدارات الأحدث، تم استبداله بسطر واحد باستخدامdefineModel.- 5 أنواع من نماذج V المخصصة: حقل الإدخال / حقل النص الموسع / قائمة الاختيار / مربع الاختيار / المُعدِّلات
- 5 مُعدِّلات: .lazy / .number / .trim / مخصصة / مُعدِّلات متعددة
- التحقق من صحة النماذج: مخصص (بسيط) / VeeValidate (معقد)
- 5 أنواع من النماذج المعقدة: المتداخلة / الديناميكية / التسلسلية / التحقق من الصحة / الملفات
- الكائنات التفاعلية + نموذج V: تقليل النصوص النمطية بنسبة 50%
- تحميل الملفات: FileReader + FormData
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
تنفيذ نموذج تسجيل دخول بسيط:
- 5 حقول (اسم المستخدم / كلمة المرور / البريد الإلكتروني / رقم الهاتف / اختبار كابتشا)
- نموذج V + التفاعلية
- طباعة كائن النموذج بالكامل عند إرساله
-
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
تنفيذ التحقق من صحة النماذج:
- 5 حقول + التحقق من الصحة المخصص (تنسيق البريد الإلكتروني، تنسيق رقم الهاتف، طول كلمة المرور)
- @blur يُطلق عملية التحقق من الصحة
- ظهور رسالة خطأ
- يتم تمكين زر الإرسال أو تعطيله بناءً على الخاصية
isValid
-
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
تنفيذ نظام نماذج «تسجيل المستخدم» متكامل:
- 10 حقول (بما في ذلك كائنات
addressالمتداخلة وarraytagsديناميكية) - VeeValidate + مخطط Yup
- تحميل الملف (صورة الملف الشخصي)
- تغطية كاملة لـ 5 عوامل معدلة
- يُستخدم
defineModelللمكونات الفرعية - 5 حالات الخطأ
- التعيين القوي للأنواع في TypeScript
- 10 حقول (بما في ذلك كائنات