Vue.js: ميزات Vue 3.4 الجديدة
آخر تحديث: 2026-08-26
يقدم إصدار Vue 3.4+ (الذي صدر في ديسمبر 2023) عدة تحسينات ثورية: defineModel تبسيط الماكرو v-model، وتفكيك الخصائص التفاعلية useTemplateRef، وواجهة برمجة التطبيقات (API) للتركيب useId، وإنشاء معرّفات فريدة. تعمل هذه الميزات على تبسيط كود template بشكل كبير وتحسين تجربة التطوير.
سيساعدك هذا الدرس على إتقان خمس ميزات جديدة أساسية ستجعل كود Vue 3 الخاص بك أكثر إيجازًا وحداثة.
1. ما ستتعلمه
defineModelماكرو لاستبدال الصيغة المعقدة لنموذج v- تفكيك المكونات التفاعلية (الافتراضي: تفكيك عميق)
useIdإنشاء معرّف فريد آمن من حيث SSRuseTemplateRefمرجع معياري- تحسينات على الرسوم المتحركة الانتقالية
- Vue 3.4 مقابل Vue 3.3 مقابل Vue 3.2: الاختلافات الرئيسية
- 5 مسارات ترقية موصى بها
2. تحدي «التزامن بين العنصر الأصلي والعنصر التابع» في أحد مكونات نموذج v
(1) المشكلة: 5 أسطر من «props» + 5 أسطر من «emit» = 10 أسطر من النص النمطي
قامت أليس بإنشاء مكون إدخال مخصص باستخدام v-model:
// ❌ The "Broken" Version:v-model Required 10 Sample Code
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
const internalValue = ref(props.modelValue)
watch(() => props.modelValue, (val) => {
internalValue.value = val
})
watch(internalValue, (val) => {
emit('update:modelValue', val)
})
10 أسطر من الكود النمطي — يجب عليك كتابة هذا لكل مكون من مكونات نموذج V.
المطور الرئيسي:
"أليس، هذا النص مطول جدًّا. يتوفر في Vue 3.4
defineModelالذي يقوم بذلك في سطر واحد."
(2) الشكل 3.4: الحل العام لماكرو defineModel
// ✅ Correct Version:1 All set v-model
const modelValue = defineModel('modelValue')
// It's possible now:
// - Read: modelValue.value
// - Write: modelValue.value = 'new value' (Automatic emit)
// - watch:watch(modelValue, ...)
// - Parent Component v-model Two-way binding works as usual
10 أسطر → سطر واحد. توفر المكونات الخمسة لنموذج V 50 سطراً من التعليمات البرمجية.
(3) الإيرادات
بعد الترقية إلى defineModel:
- حجم الكود: 10 أسطر → سطر واحد (-90٪)
- سهولة القراءة: المقصود واضح في سطر واحد
- استنتاج الأنواع: تقوم TypeScript باستنتاج الأنواع تلقائيًا
- قابل للتوسيع: يدعم transform و localStorage وغيرها
3. شرح مفصل لماكرو defineModel
(1) الاستخدام الأساسي
<!-- Child component:CustomInput.vue -->
<template>
<input :value="modelValue" @input="modelValue = $event.target.value">
</template>
<script setup>
// ✅ 1-line v-model
const modelValue = defineModel('modelValue')
</script>
<!-- Parent Component:Usage v-model -->
<template>
<CustomInput v-model="searchQuery" />
<p>Search: {{ searchQuery }}</p>
</template>
<script setup>
import { ref } from 'vue'
const searchQuery = ref('')
</script>
(2) 5 استخدامات متقدمة
// 1. Default value
const modelValue = defineModel('modelValue', { default: '' })
// 2. Type Constraints(TypeScript)
const count = defineModel<number>('count', { default: 0 })
// 3. transform Convert(Conversion at the Time of Writing)
const trimmed = defineModel('text', {
set(value) {
return value.trim() // Automatic trim
}
})
// 4. getter(Conversion During Reading)
const upper = defineModel('text', {
get(value) {
return value.toUpperCase() // Always uppercase
}
})
// 5. Several v-model
const name = defineModel('name')
const email = defineModel('email')
const phone = defineModel('phone')
(3) مثال كامل
<!-- Child component:TrimInput.vue -->
<template>
<input :value="value" @input="value = $evento.target.value">
</template>
<script setup>
const value = defineModel('value', {
// get: Conversion During Reading
get(val) {
return val?.toUpperCase() || ''
},
// set: Conversion at the Time of Writing
set(val) {
return val?.trim() || ''
},
default: ''
})
</script>
<!-- Parent Component -->
<CustomInput v-model="name" />
<!-- User Input "alice" → trim Unchanged → uppercase → "ALICE" -->
4. تفكيك الخصائص التفاعلية
(1) المشكلات التي كانت موجودة قبل إصدار Vue 3.4
// ❌ Vue 3.3 and before that
const props = defineProps({ count: Number, name: String })
// Use directly in the template props.count
// JS Key Points props.count
// Deconstruction may result in a loss of response
const { count, name } = props // count/name Convert to a regular value
(2) Vue 3.4: تفكيك المتغيرات مع الحفاظ على سرعة الاستجابة
<!-- Child component -->
<template>
<p>{{ count }} - {{ name }}</p>
<button @click="count++">+1</button>
</template>
<script setup>
// ✅ Vue 3.4:Remains responsive even after deconstruction
const { count, name } = defineProps<{
count: number
name: string
}>()
</script>
تظل العناصر المساعدة المُفككة تفاعلية! وهذا أحد أكبر التحسينات في Vue 3.4.
(3) 5 سيناريوهات رئيسية للتطبيق
// 1. Simplify props Usage
const { count, name } = defineProps<{ count: numbervalue; name: string }>()
// 2. Deconstruction + Rename
const { count: total, name: userName } = defineProps<{ count: numbervalue; name: string }>()
// 3. Deconstruction + Default value
const { count = 0, name = 'Guest' } = defineProps<{ count?: numbervalue; name?: string }>()
// 4. In conjunction with watch(After deconstruction watch Still working)
const { count } = defineProps<{ count: numbervalue }>()
watch(count, (newVal) => console.log('count changed:', newVal))
// 5. In conjunction with computed(Stay responsive as well)
const { count } = defineProps<{ count: numbervalue }>()
const double = computed(() => count * 2)
(4) ملاحظات مهمة
// ⚠️ Deconstructed props Cannot be reassigned
const { count } = defineProps<{ count: number }>()
count = 10 // ❌ Error:Assignment to constant variable
// ✅ Correct:Via emit Have the parent component modify
const emit = defineEmits(['update:count'])
const handleClick = () => emit('update:count', count + 1)
5. useId: المعرّف الفريد
(1) لماذا نحتاج إلى useId؟
أثناء عملية العرض من جانب الخادم (SSR)، قد تتسبب المعرّفات المكتوبة يدويًّا في حدوث تعارضات (استخدام عدة مثيلات للمكون نفسه باستخدام المعرّف نفسه). يقوم useId بإنشاء معرّفات فريدة عالميًّا وآمنة للاستخدام مع SSR.
// ❌ Old notation:Handwritten ID,SSR There will be a conflict
const id = `input-${Math.random()}`
// ✅ Vue 3.4+:useId
import { useId } from 'vue'
const id = useId() // 'v-0', 'v-1', ...
(2) 5 حالات استخدام رئيسية
<!-- 1. Form label Relationship -->
<template>
<label :for="id">Username</label>
<input :id="id" v-model="username">
</template>
<script setup>
import { useId } from 'vue'
const id = useId()
const username = ref('')
</script>
<!-- 2. ARIA Properties -->
<template>
<button :aria-describedby="`${id}-help`">Click</button>
<p :id="`${id}-help`">This button does X</p>
</template>
<!-- 3. Form Error Messages -->
<template>
<input v-model="email" :aria-invalid="!!error" :aria-errormessage="`${id}-error`">
<p v-if="error" :id="`${id}-error`">{{ error }}</p>
</template>
<!-- 4. Tooltip Relationship -->
<template>
<button :aria-describedby="`${id}-tip`">?</button>
<span :id="`${id}-tip`" class="tooltip">Help text</span>
</template>
<!-- 5. Modal Window -->
<template>
<div :id="`${id}-modal`" role="dialog" aria-modal="true">
...
</div>
</template>
(3) 5 مزايا رئيسية
| الميزة | الوصف |
|---|---|
| أمان SSR | مطابقة معرّف الخادم/العميل |
| فريد | لا يحدث تعارض بين المثيلات المتعددة في نفس الصفحة |
| مستقر | لا يزال إعادة عرض المكونات دون تغيير |
| قابل للقراءة | تتضمن المعرّفات البادئة v- لتسهيل التعرف عليها |
| خفيف الوزن | 0 تبعيات (مُدمجة في Vue) |
6. useTemplateRef (شرح مفصل لـ Vue 3.5+)
(1) الميزات الكاملة
<template>
<input ref="usernameInput">
<MyChart ref="chartComponent" :data="chartData" />
</template>
<script setup lang="ts">
import { useTemplateRef, onMounted } from 'vue'
import MyChart from './MyChart.vue'
// ✅ Vue 3.5+:One Name Does It All
const inputRef = useTemplateRef<HTMLInputElement>('usernameInput')
const chartRef = useTemplateRef<InstanceType<typeof MyChart>>('chartComponent')
// ✅ TypeScript Perfect Inference
onMounted(() => {
inputRef.value?.focus() // HTMLInputElement Type
chartRef.value?.refresh() // Component Instance Methods
})
</script>
(2) مقارنة بين Vue 3.5 و Vue 3.4
| البعد | متغير ref في Vue 3.4 | useTemplateRef في Vue 3.5 |
|---|---|---|
| مرجع القالب | <input ref="inputRef"> |
<input ref="usernameInput"> |
| مرجع JS | const inputRef = ref(null) |
const inputRef = useTemplateRef('usernameInput') |
| استدلال الأنواع | يدوي ref<HTMLInputElement | null> |
الاستدلال التلقائي |
| اتساق الأسماء | لا يتم الإبلاغ عن الأخطاء الإملائية المحتملة | يتم الإبلاغ عن حالات عدم الاتساق |
| الإيجاز | سطران | سطر واحد |
7. 5 مسارات الترقية الموصى بها
(1) وقت الترقية
| إصدار Vue | تاريخ الإصدار | الstatus |
|---|---|---|
| Vue 3.0 | سبتمبر 2020 | إصدار قديم (يُنصح بالترقية) |
| Vue 3.2 | أغسطس 2021 | إصدار مستقر |
| Vue 3.3 | مايو 2023 | إصدار مستقر |
| Vue 3.4 | ديسمبر 2023 | موصى به حاليًا |
| Vue 3.5 | سبتمبر 2024 | أحدث إصدار مستقر |
(2) قائمة الترقيات
✅ Vue 3.4:
- defineModel Macro Replacement v-model Complex Notation
- Responsive props Deconstruction
- Abbreviations with the Same Name(<input :value> Replace :value="value")
- Improvements to Error Handling
- Template Parser Override(2x Performance)
✅ Vue 3.5:
- useTemplateRef(Recommendations)
- Responsive props Deconstruction and Improvement
- useId Stable
- Deferred teleport
- Improved Suspense
(3) توصيات الترقية
| السيناريو | التوصية |
|---|---|
| مشروع جديد | استخدام Vue 3.5+ مباشرةً |
| مشروع قديم (Vue 3.3–) | الترقية إلى الإصدار 3.5، الترحيل حسب الميزة |
| مشروع قديم (Vue 3.0–3.2) | الترقية إلى الإصدار 3.3، ثم إلى الإصدار 3.5 |
| المشاريع الضخمة | التحديثات التدريجية (تحديث Vue أولاً، ثم ترحيل v-model، وما إلى ذلك) |
8. مثال كامل: تطبيق شامل باستخدام Vue 3.4+
▶ مثال: 1. 5 طرق لاستخدام defineModel
<!-- 1. Basics -->
<script setup>
const modelValue = defineModel('modelValue')
</script>
<!-- 2. Default value -->
<script setup>
const count = defineModel('count', { default: 0 })
</script>
<!-- 3. TypeScript Type -->
<script setup lang="ts">
const user = defineModel<User>('user', { default: () => ({}) })
</script>
<!-- 4. transform Convert -->
<script setup>
const trimmed = defineModel('text', {
set(val) { return val?.trim() || '' }
})
</script>
<!-- 5. Several v-model -->
<script setup>
const username = defineModel('username')
const password = defineModel('password')
</script>
▶ مثال: 2. تفكيك الخصائص التفاعلية
<template>
<p>{{ count }} - {{ name }}</p>
<button @click="emit('update:count', count + 1)">+1</button>
</template>
<script setup lang="ts">
// ✅ Vue 3.4+:Deconstructing Responsiveness
const { count, name } = defineProps<{
count: number
name: string
}>()
const emit = defineEmits<{
'update:count': [value: number]
}>()
</script>
▶ مثال: 3. حالات الاستخدام الرئيسية لـ useId 5
<template>
<label :for="id">Email</label>
<input :id="id" v-model="email" :aria-invalid="!!error" :aria-errormessage="`${id}-error`">
<p v-if="error" :id="`${id}-error`" class="error">{{ error }}</p>
</template>
<script setup>
import { ref, useId } from 'vue'
const id = useId() // v-0, v-1, ...
const email = ref('')
const error = ref('')
</script>
▶ مثال: 4. استكمال الطلب باستخدام useTemplateRef
<template>
<input ref="usernameInput" v-model="username">
<MyChart ref="chartComponent" :data="chartData" />
</template>
<script setup lang="ts">
import { ref, useTemplateRef, onMounted } from 'vue'
import MyChart from './MyChart.vue'
const username = ref('')
const chartData = ref([1, 2, 3])
// ✅ Vue 3.5+:TypeScript Perfect Inference
const inputRef = useTemplateRef<HTMLInputElement>('usernameInput')
const chartRef = useTemplateRef<InstanceType<typeof MyChart>>('chartComponent')
onMounted(() => {
inputRef.value?.focus()
chartRef.value?.refresh()
})
</script>
▶ مثال: 5. مرجع سريع لـ 5 أخطاء شائعة
| الخطأ | الأعراض | الحل |
|---|---|---|
| تفكيك defineModel | لا توجد استجابة | استخدم const modelValue = defineModel() مباشرةً |
| التعيين بعد تفكيك خصائص props | خطأ | استخدم emit للسماح للعنصر الأصلي بالتحديث |
| useId مع سلسلة نصية مبرمجة ثابتًا | تعارض في SSR | التبديل إلى useId() |
اسم useTemplateRef غير صحيح |
ref.value قيمة فارغة |
يجب أن يتطابق الاسم |
| يتطلب الترقية إلى Vue 3.4+ تغييرات كثيرة جدًّا | مشكلات التوافق | الترحيل التدريجي |
▶ مثال: 6. 5 مزايا رئيسية للترقية إلى Vue 3.4 أو أحدث
| الإيرادات | البيانات |
|---|---|
| تقليل حجم الكود | 30–50% (تبسيط defineModel) |
| تحسينات في الأداء | تحليل القوالب أسرع بمقدار الضعف |
| استدلال الأنواع | دقة 100% (TypeScript) |
| متوافق مع SSR | استخدام useId لحل تعارضات المعرّفات |
| تجربة المطور | تحسينات كبيرة في تجربة المطور |
❓ أسئلة شائعة
defineModel محل الصيغة الكاملة لـ v-model؟defineModel('modelValue') is equivalent to the 10 lines of code in props.modelValue + emit('update:modelValue'). وهو متوافق تمامًا مع الإصدارات السابقة.useId مع كل عملية عرض؟useId ثابتًا طوال دورة حياة المكون (متسقًا بين SSR والعميل).defineProps مع watch؟const { count } = defineProps() + watch(count, ...) بشكل مثالي.npm update vue@latest. على مستوى الكود: استخدم defineModel بدلاً من بناء الجملة المعقد v-model؛ واستخدم تفكيك المتغيرات التفاعلي بدلاً من props.xxx. دون أي أعطال.defineProps، والاستدلال التام على الأنواع باستخدام useTemplateRef، ودعم أكثر شمولاً لـ TypeScript فيما يتعلق بالماكرو. نوصي باستخدام <script setup lang="ts">.📖 ملخص
- الماكرو
defineModel: سطر واحد منv-modelيحل محل 10 أسطر من الكود النمطي - تفكيك عناصر الـ props التفاعلية: يستخدم Vue 3.4+ التفاعلية العميقة بشكل افتراضي
- useId: معرّف فريد آمن للاستخدام في SSR
- useTemplateRef: مرجع قابل للتركيب في Vue 3.5 وما بعده، واستدلال قوي للأنواع
- تحديث Vue 3.4:
defineModel، التفكيك التفاعلي، والاختصارات ذات الأسماء المتطابقة - تحديث Vue 3.5: أصبحت ميزتا useTemplateRef وuseId مستقرتين الآن
- التوصية: استخدم Vue 3.5 أو أحدث مباشرةً
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
إعادة هيكلة مكون نموذج v باستخدام
defineModel:- المكون الفرعي CustomInput.vue: سطر واحد من
defineModel - المكون الأصلي: الربط ثنائي الاتجاه في نموذج v
- يدعم القيم الافتراضية
- المكون الفرعي CustomInput.vue: سطر واحد من
-
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
إعادة كتابة المكونات باستخدام التفكيك التفاعلي في Vue 3.4+:
- تفكيك الخصائص مثل «count» و«name» وغيرها
- باستخدام الساعة + الحساب
- اختبار التصميم المتجاوب
-
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
مثال على عملية ترقية كاملة إلى Vue 3.4+:
- استبدال بناء الجملة المعقد لنموذج V بـ
defineModelفي 5 مكونات - تفكيك 3 مكونات باستخدام عناصر تفاعلية
- 5 مكونات تستخدم
useId(لربطlabelوinput) - 3 مكونات تستخدم
useTemplateRef(Vue 3.5+) - 5 مكونات TypeScript ذات الأنواع القوية
- اختبار اتساق
useIdفي سيناريو SSR
- استبدال بناء الجملة المعقد لنموذج V بـ