Vue.js: قواعد القوالب
آخر تحديث: 2026-08-26
تعد صيغة «template» في Vue امتدادًا إعلانيًا لـ HTML — فهي تضيف «التوجيهات» و«الاستيفاء» إلى HTML القياسي، مما يتيح لك عرض البيانات في DOM. تتشابه صيغة «template» بأكملها بنسبة 90% مع HTML، لذا يمكن لأي شخص على دراية بـ HTML البدء في استخدامها خلال 30 دقيقة.
تتمحور قوالب Vue حول ثلاثة عناصر أساسية: الاستيفاء (عرض البيانات) + التوجيهات (ربط الخصائص/الأحداث) + التعبيرات (تنفيذ جافا سكريبت). سيرشدك هذا الدرس إلى الطرق القياسية لاستخدام هذه العناصر الثلاثة.
1. ما ستتعلمه
- 4 طرق لاستخدام استيفاء «المستاش»
{{ }} - الاختلافات بين التوجيهات النصية الثلاثة: v-text و v-html و v-once
- v-bind: اختصار لـ «ربط السمات»
:وصيغة «object» - v-on: اختصار لـ «ربط الحدث»
@والمعدِّلات - القيود المفروضة على تعبيرات JS المتاحة في القوالب (لا يُسمح باستخدام الجمل)
- أغراض التوجيهين الحدوديين، v-pre و v-cloak
2. متطلبات عرض بطاقة منتج التجارة الإلكترونية
(1) المشكلة: 30 منتجًا — هل يجب النسخ واللصق 30 مرة؟
تقوم أليس بإنشاء لوحة تحكم إدارية لموقع تجارة إلكترونية. وهي بحاجة إلى عرض 30 بطاقة منتج. نهجها الأولي هو:
<!-- Copy and paste 30 times, nightmare... -->
<div class="product-card">
<h3>iPhone 15 Pro</h3>
<p>Price: $999</p>
<button>Edit</button>
</div>
<div class="product-card">
<h3>MacBook Pro</h3>
<p>Price: $2499</p>
<button>Edit</button>
</div>
<!-- ... And also 28 more ... -->
عندما يقول مدير المنتج تشارلي: «غيّر تنسيق السعر ليظهر رمز العملة»:
«أليس، لا يمكنك تعديل 30 ملفًا يدويًّا. وماذا لو كان لدينا 5,000 منتج الشهر المقبل؟ نحن بحاجة إلى قالب ديناميكي.»
(2) حل القوالب في Vue: مكون واحد، 30 نقطة بيانات
<template>
<div v-for="product in products" :key="product.id" class="product-card">
<h3>{{ product.name }}</h3>
<p>Price: ${{ product.price }}</p>
<button @click="editProduct(product.id)">Edit</button>
</div>
</template>
<script setup>
const products = ref([
{ id: 1, name: 'iPhone 15 Pro', price: 999 },
{ id: 2, name: 'MacBook Pro', price: 2499 },
// ... 30 items, each only 1 line 1 ...
])
function editProduct(id) {
console.log('Editing product', id)
}
</script>
5 أسطر من كود القالب × 30 منتجًا = لا حاجة إلى النسخ واللصق. وعندما تتغير أسماء المنتجات أو أسعارها، يقوم Vue بتحديث DOM تلقائيًا.
(3) الإيرادات
بعد اعتماد قوالب Vue:
- حجم الكود: 30 × 15 سطراً = 450 سطراً → قالب واحد + 30 قيمة = 60 سطراً (-87%)
- تكاليف الصيانة: تغيير نمط قالب واحد → يتم تحديث 30 منتجًا في آن واحد
- إضافة منتج جديد: أضف صفًا واحدًا من البيانات → يتم عرض القالب تلقائيًا
3. الاستيفاء: Mustache {{ }}
(1) 4 طرق لتنفيذ الاستيفاء
| الصيغة | النتيجة | مثال |
|---|---|---|
{{ var }} |
عرض قيمة المتغير | <p>{{ username }}</p> → أليس |
{{ expr }} |
عرض نتيجة تعبير JS | <p>{{ count + 1 }}</p> → 6 |
{{ fn() }} |
قيمة الإرجاع لنداء الدالة | <p>{{ formatDate(date) }}</p> → 2 يوليو 2026 |
{{ obj.prop }} |
الوصول إلى خصائص الكائن | <p>{{ user.name }}</p> → أليس |
(2) الخصائص الخمس الرئيسية لعملية الاستيفاء
<template>
<!-- 1. Automatically Responsive:Automatically updates when variables change -->
<p>{{ message }}</p>
<!-- 2. Support JS Expression(Cannot write statements)-->
<p>{{ count * 2 + 1 }}</p>
<p>{{ isVip ? 'VIP' : 'Regular' }}</p>
<p>{{ items.length > 0 ? items[0].name : 'Empty' }}</p>
<!-- 3. Supports ternary expressions,Arithmetic,Function Call -->
<p>{{ new Date().getFullYear() }}</p>
<!-- 4. Not supported: if/for/Variable Declaration (Use v-if/v-for instead) -->
<!-- ❌ {{ if (x) { y } }} // Wrong -->
<!-- ✅ <p v-if=\"x\">y</p> // Correct -->
<!-- 5. Support for template strings -->
<p>{{ `Hello, ${name}!` }}</p>
</template>
(3) الفرق بين الاستيفاء وـ v-text
<template>
<!-- Interpolation:You can add other content here -->
<p>Welcome, {{ name }}!</p>
<!-- v-text:Replace the entire element's content -->
<p v-text="`Welcome, ${name}!`"></p>
<!-- The above is equivalent to <p>Welcome, {{ name }}!</p> -->
</template>
4. توجيهات النص: v-text / v-html / v-once
(1) مقارنة بين الأوامر الثلاثة
| الأمر | الوظيفة | status الاستخدام | الأمان |
|---|---|---|---|
{{ }} الاستيفاء |
استيفاء النص | الإعداد الافتراضي | ✅ الأمان |
v-text |
تعيين خاصية textContent لعنصر ما | صيغة بديلة للتقريب | ✅ آمن |
v-html |
تعيين قيمة innerHTML لعنصر ما | عرض النص المنسق (يُستخدم بحذر) | ⚠️ خطر XSS |
v-once |
عرض مرة واحدة، دون تحديثات لاحقة | تحسين ثابت | ✅ الأمان |
(2) تحذير v-html
<template>
<!-- ✅ Safety:Content You Control -->
<div v-html="markdownHtml"></div>
<!-- ❌ Danger:User-entered HTML -->
<div v-html="userComment"></div>
<!-- User Input <script>alert('hacked')</script> Will be executed! -->
<!-- ✅ Recommended: Use DOMPurify Cleanup -->
<div v-html="DOMPurify.sanitize(userComment)"></div>
</template>
(3) تحسين أداء v-once
<template>
<!-- This productId won't change → Use v-once to Reduce Reactive Overhead -->
<span v-once>{{ productId }}</span>
<!-- But this currentPrice Will change → Do not use v-once -->
<span>{{ currentPrice }}</span>
</template>
5. ربط الخصائص: v-bind والاختصار :
(1) أساسيات v-bind
<template>
<!-- Complete Syntax -->
<img v-bind:src="imageUrl" v-bind:alt="imageAlt">
<!-- Abbreviation(Recommendations):Remove v-bind,Only : -->
<img :src="imageUrl" :alt="imageAlt">
<!-- Dynamic property names(v-bind Special Syntax)-->
<img :[dynamicAttr]="value">
</template>
(2) ربط الفئة والأسلوب (نقطة أساسية)
<template>
<!-- 1. Object Syntax:Switch Based on Conditions class -->
<div :class="{ active: isActive, 'text-danger': hasError }">
Conditional classes
</div>
<!-- 2. Array Syntax:Apply multiple at the same time class -->
<div :class="[activeClass, errorClass]">
Multiple classes
</div>
<!-- 3. Trinomial Expression(Array + Object)-->
<div :class="[isActive ? 'active' : '', { 'has-error': hasError }]">
Mixed
</div>
<!-- 4. Nested Objects in an Array -->
<div :class="[{ active: isActive }, errorClass]">
Nested
</div>
</template>
(3) أسلوب الربط
<template>
<!-- 1. Object Syntax -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">
Styled
</div>
<!-- 2. Array Syntax(Merge multiple style Object)-->
<div :style="[baseStyles, overridingStyles]">
Multi styles
</div>
<!-- 3. CSS Variable(Vue 3 Recommendations)-->
<div :style="{ '--main-color': mainColor }">
With CSS variables
</div>
</template>
6. ربط الأحداث: v-on والاختصار @
(1) أساسيات v-on
<template>
<!-- Complete Syntax -->
<button v-on:click="handleClick">Click</button>
<!-- Abbreviation(Recommendations)-->
<button @click="handleClick">Click</button>
<!-- Inline Processor(Write the simple logic directly)-->
<button @click="count++">Increment</button>
<!-- Passing Parameters -->
<button @click="handleClick('arg1', $event)">Click</button>
</template>
(2) مُعدِّلات الأحداث الشائعة (9)
<template>
<!-- 1. .stop:Prevent Bubbling(e.stopPropagation())-->
<button @click.stop="handleClick">Stop propagation</button>
<!-- 2. .prevent:Prevent the default behavior(e.preventDefault())-->
<form @submit.prevent="handleSubmit">Submit</form>
<!-- 3. .once:Triggered only once -->
<button @click.once="handleClick">Click once</button>
<!-- 4. .self:Only event.target This is triggered only if it is the current element -->
<div @click.self="handleClick">Self only</div>
<!-- 5. .capture:Using the Capture Phase -->
<div @click.capture="handleClick">Capture</div>
<!-- 6. .passive:Do not prevent the default behavior(Performance Optimization)-->
<div @scroll.passive="onScroll">Passive scroll</div>
<!-- 7. Keyboard Modifiers -->
<input @keyup.enter="submit"> <!-- Enter Key -->
<input @keyup.esc="cancel"> <!-- Esc Key -->
<input @keyup.tab="nextField"> <!-- Tab Key -->
<input @keyup.delete="deleteItem"> <!-- Delete Key -->
<!-- 8. Mouse Modifiers -->
<button @click.left="leftClick">Left</button>
<button @click.right.prevent="rightClick">No right click</button>
<button @click.middle="middleClick">Middle</button>
<!-- 9. System Modifiers(Key combinations)-->
<button @click.ctrl="save">Ctrl + Click</button>
<button @click.ctrl.exact="save">Accurate Ctrl(You cannot add other keys)</button>
</template>
(3) سلاسل المُعدِّلات
<template>
<!-- Combinations of Multiple Modifiers(Execute in order)-->
<button @click.stop.prevent="handle">Stop + Prevent</button>
<!-- is effectively equivalent to -->
<button @click="(e) => { e.stopPropagation(); e.preventDefault(); handle(); }">
Manual
</button>
</template>
7. قيود التعبير (لا يُسمح بإبداء الآراء)
(1) تعبيرات القوالب مقابل جمل جافا سكريبت
<template>
<!-- ✅ Sure.(Expression,Has a return value)-->
<p>{{ count + 1 }}</p>
<p>{{ isVip ? 'VIP' : 'Regular' }}</p>
<p>{{ items.filter(i => i.active).length }}</p>
<!-- ❌ That won't work.(Statement,No return value)-->
<!-- <p>{{ const x = 1 }}</p> // Wrong: Variable Declaration -->
<!-- <p>{{ if (x) { y } }}</p> // Wrong: if Statement -->
<!-- <p>{{ for (i of arr) {} }}</p> // Wrong: for Loop -->
<!-- ✅ Switch to v-if / v-for Instructions -->
<p v-if="x">y</p>
<div v-for="i in arr" :key="i.id">{{ i.name }}</div>
</template>
(2) التعبيرات مقابل الخصائص المحسوبة
| السيناريو | استخدام التعبيرات | استخدام الخصائص المحسوبة |
|---|---|---|
| العمليات الحسابية البسيطة (1–2 علامات حسابية) | ✅ | ❌ |
| منطق معقد (متعدد الخطوات، حلقات) | ❌ | ✅ |
| التخزين المؤقت مطلوب | ❌ | ✅ |
| المعلمات المطلوبة | ✅ | ❌ (استخدم طريقة بدلاً من ذلك) |
<template>
<!-- Simple:Expression -->
<p>{{ price * quantity }}</p>
<!-- Complex:Computed Properties(computed)-->
<p>{{ totalWithTax }}</p>
</template>
8. توجيهات Edge: v-pre / v-cloak
(1) v-pre: تخطي عملية الترجمة
<template>
<!-- Skip compiling this element,{{ }} Display as is -->
<span v-pre>{{ this is literal, not interpolated }}</span>
</template>
السيناريو: توضيح بناء جملة قوالب Vue نفسها (مثل الدروس التعليمية ومواقع الوثائق).
(2) v-cloak: إخفاء قبل التحويل البرمجي
<template>
<!-- v-cloak Make elements invisible until compilation is complete -->
<p v-cloak>{{ message }}</p>
</template>
<style>
[v-cloak] { display: none; }
</style>
السيناريو: منع المستخدمين من رؤية {{ message }} تومض قبل التجميع.
9. مثال كامل: بطاقات المنتجات الديناميكية
▶ مثال: 1. بطاقة المنتج الأساسية
<template>
<div class="product-card">
<h3>{{ product.name }}</h3>
<p class="price">${{ product.price }}</p>
<span :class="['badge', stockStatusClass]">
{{ stockStatusText }}
</span>
<button :disabled="product.stock === 0" @click="addToCart">
Add to Cart
</button>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
product: { type: Object, required: true }
})
const stockStatusClass = computed(() => {
if (props.product.stock === 0) return 'out-of-stock'
if (props.product.stock < 10) return 'low-stock'
return 'in-stock'
})
const stockStatusText = computed(() => {
if (props.product.stock === 0) return 'Out of Stock'
if (props.product.stock < 10) return `Only ${props.product.stock} left`
return 'In Stock'
})
function addToCart() {
console.log('Added', props.product.name)
}
</script>
▶ مثال: 2. طرق مختلفة لكتابة عملية الاستيفاء
<template>
<!-- Variable Interpolation -->
<p>{{ username }}</p>
<!-- Expression Interpolation -->
<p>{{ count * 2 + 1 }}</p>
<!-- Trinomial Expression -->
<p v-if="score >= 60">Passed</p>
<p v-else>Failed</p>
<!-- Function Call -->
<p>{{ formatDate(new Date()) }}</p>
<!-- Object Access -->
<p>{{ user.profile.name }}</p>
</template>
▶ مثال: 3. 5 طرق لاستخدام v-bind
<template>
<!-- 1. Basic Binding -->
<img :src="imageUrl">
<!-- 2. Dynamic property names -->
<img :[attrName]="value">
<!-- 3. Object Syntax(class)-->
<div :class="{ active: isActive }">A</div>
<!-- 4. Array Syntax(class)-->
<div :class="[activeClass, errorClass]">B</div>
<!-- 5. String Concatenation(href)-->
<a :href="`/users/${userId}`">Profile</a>
</template>
▶ مثال: 4. 5 طرق لكتابة v-on
<template>
<!-- 1. Just a click -->
<button @click="handleClick">Click</button>
<!-- 2. Inline Statements -->
<button @click="count++">+</button>
<!-- 3. Passing Parameters + event Object -->
<button @click="handleDelete(item.id, $event)">Delete</button>
<!-- 4. Modifier Chain -->
<button @click.stop.prevent="handle">Save</button>
<!-- 5. Keyboard shortcuts -->
<input @keyup.ctrl.enter="submit">
</template>
▶ مثال: 5. القوالب مقابل HTML — مرجع سريع
| العنصر | صيغة HTML | صيغة Vue | الوصف |
|---|---|---|---|
| نص ثابت | <p>Hello</p> |
<p>{{ msg }}</p> |
تحويل إلى نص ديناميكي |
| خاصية ثابتة | <img src="x.jpg"> |
<img :src="url"> |
التغيير إلى ديناميكي |
| فئة ثابتة | <div class="box"> |
<div :class="cls"> |
التغيير إلى ديناميكية |
| النمط الثابت | <div style="color: red"> |
<div :style="sty"> |
التبديل إلى النمط الديناميكي |
| حدث النقر | <button onclick="fn()"> |
<button @click="fn"> |
تعديل Vue |
| العرض الشرطي | لا شيء | <div v-if="ok"> |
خاص بـ Vue |
| عرض القائمة | لا شيء | <div v-for="i in list"> |
خاص بـ Vue |
▶ مثال: 6. 5 أخطاء شائعة
| الخطأ | الأعراض | الحل |
|---|---|---|
احذف .value |
اكتب {{ count }} في القالب، وcount.value في ملف JS |
يقوم القالب بفك التعبئة تلقائيًا؛ أما ملف JS فيحتاج إلى .value |
كتابة if/for في {{ }} |
أخطاء الترجمة | استخدام توجيهات v-if / v-for |
| v-html XSS | مدخلات المستخدم <script> التنفيذ |
التنظيف باستخدام DOMPurify |
| لم تتلقَ الدالة المضمنة $event | يتعذر الوصول إلى كائن الحدث | @click="fn($event)" قم بتمريره صراحةً |
| ترتيب المُعدِّلات غير صحيح | فشل في منع انتقال التأثير | .stop يسبق .prevent (حسب الحاجة) |
❓ أسئلة شائعة
{{ }} و v-text؟textContent للعنصر. الفرق هو أن {{ }} يمكن استخدامه جنبًا إلى جنب مع النص الثابت (<p>Hi, {{ name }}</p>)، بينما يجب أن يشغل v-text العنصر بأكمله (<p v-text="msg"></p>). نوصي باستخدام {{ }} لمزيد من المرونة.<script> لمهاجمة موقعك. ما لم تكن تثق بنسبة 100% في مصدر البيانات (مثل أداة عرض Markdown قمت بكتابتها بنفسك)، استخدم {{ }} للتقريب أو قم بتنقية البيانات أولاً باستخدام DOMPurify.sanitize().@click.stop.prevent أولاً stopPropagation()، ثم preventDefault().v-bind؟ وهل يمكن اختصار v-on أيضًا؟v-bind هو : (بدون v-bind)، واختصار v-on هو @ (موصى به). كلا الاختصارين هما ترميزات رسمية مدعومة منذ الإصدار Vue 2، وتدعمهما ميزة تمييز الصيغة في بيئة تطوير المتكاملة (IDE) وميزة الإكمال التلقائي.:class="{ active: isActive, 'text-danger': hasError }". أو صيغة الarray :class="[activeClass, errorClass]". يمكنك المزج بين الصيغتين: :class="[isActive && 'active', { 'error': hasError }]".@keyup.ctrl.s="save" أو @keydown.ctrl.s.prevent="save" (أضف .prevent لمنع ظهور مربع حوار الحفظ الافتراضي للمتصفح). يدعم Vue 3 أيضًا المُعدِّل الدقيق .exact؛ على سبيل المثال، يتطلب @click.ctrl.exact الضغط على مفتاح Ctrl فقط — ولا يُسمح بالضغط على أي مفاتيح أخرى.📖 ملخص
- تُعد صيغة قوالب Vue امتدادًا إعلانيًا لـ HTML، وتركز على الاستيفاء والتوجيهات والتعبيرات.
- يدعم «Mustache Interpolation»
{{ }}المتغيرات، والتعبيرات، واستدعاءات الدوال، والمشغلات الثلاثية - لا يجوز أن تحتوي تعبيرات القوالب على عبارات if/for أو إعلانات المتغيرات (استخدم التوجيهات v-if و v-for بدلاً من ذلك)
- يستبدل v-text النص الكامل للعنصر؛ بينما يعرض v-html النص المنسق (خطر XSS)
- تحسين أداء v-once (يتم عرض المحتوى الثابت مرة واحدة فقط)، وv-pre يتخطى عملية التجميع
- v-bind (يُختصر بـ
:)، و v-on (يُختصر بـ@)، و3 طرق شائعة لربط الفئات - 9 مُعدِّلات للأحداث: .stop / .prevent / .once / .self / .capture / .passive + لوحة المفاتيح/الماوس/النظام
- الاختيار بين التعبيرات النموذجية والخصائص المحسوبة: استخدم التعبيرات في الحالات البسيطة، والخصائص المحسوبة في الحالات المعقدة
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
اكتب مكونًا في Vue 3 يعرض بطاقة مستخدم تحتوي على:
- اسم المستخدم (الاستيفاء)
- صورة الملف الشخصي (v-bind)
- زر «متابعة» (v-on)
- status الاتصال/عدم الاتصال (زر تبديل ديناميكي)
البيانات المستخدمة: كائن المستخدم
{ name: 'Alice', avatar: 'alice.jpg', online: true } -
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
قم بتطبيق قائمة مهام بسيطة:
- حقل الإدخال (لا يُستخدم نموذج v في الوقت الحالي؛ يتم محاكاته باستخدام v-bind:value + @input)
- زر «إضافة» (@click + تعبير)
- عرض القوائم (v-for؛ سنتناول هذا الموضوع في الدرس التالي)
- يحتوي كل عنصر على زر حذف (@click + تمرير المعلمات)
البيانات:
const todos = ref(['Learn Vue', 'Build app', 'Deploy']) -
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
اكتب مكونًا كاملاً لبطاقة تفاصيل المنتج يدعم ما يلي:
- 5 شارات لstatus المنتج (متوفر/غير متوفر/طلب مسبق/معروض للبيع/تم إيقاف إنتاجه)
- تتوافق 5 حالات مع ألوان فئات مختلفة (يتم ربطها بشكل جماعي باستخدام صيغة الكائنات)
- أزرار زيادة/تقليل الكمية (النقر عليها يؤدي إلى الزيادة/التقليل)
- تعطيل زر «إضافة إلى سلة التسوق» عندما تكون الكمية أكبر من أو تساوي الكمية المتوفرة في المخزون (:يتم تعطيله ديناميكيًا)
- ما لا يقل عن 3 مُعدِّلات للأحداث (@click.stop، @submit.prevent، إلخ)