Vue.js: أساسيات المكونات

آخر تحديث: 2026-08-26

مكونات Vue هي مثيلات Vue قابلة لإعادة الاستخدام — حيث يحتوي كل مكون على نموذجه الخاص ومنطقه وأنماطه. وتتمثل البنية الأساسية لـ Vue 3 في «شجرة المكونات»: المكون الجذري (App.vue) → مكونات الصفحات → مكونات الأعمال → المكونات الأساسية (Button/Input).

يُعد إتقان أساسيات المكونات خطوة حاسمة في بناء تطبيقات Vue واسعة النطاق. سيساعدك هذا الدرس على فهم طبيعة المكونات، وكيفية تسجيلها، وقواعد تسميتها، ولماذا تُعد مكونات الملف الواحد (SFCs) هي النهج الموصى به في Vue.

1. ما ستتعلمه



2. نظام خلفي للتجارة الإلكترونية من 30 صفحة يعاني من تكرار الأكواد

(1) المشكلة: 30 بطاقة منتج، و30 عملية نسخ ولصق

كانت مسؤولة التجارة الإلكترونية لدى أليس بحاجة إلى عرض 30 بطاقة منتج على عدة صفحات. وكانت طريقتها الأولية هي:

HTML
<!-- ❌ The "Broken" Version: Copy Product Card 30 times -->
<!-- In HomeView.vue -->
<div class="product-card">
  <img src="iphone.jpg">
  <h3>iPhone 15 Pro</h3>
  <p>$999</p>
  <button>Add to Cart</button>
</div>
<div class="product-card">
  <img src="macbook.jpg">
  <h3>MacBook Pro</h3>
  <p>$2499</p>
  <button>Add to Cart</button>
</div>
<!-- ... And also 28 more ... -->

<!-- The same code appears again in ProductList.vue, SearchResults.vue, Cart.vue -->

يضيف مدير المنتج تشارلي 5 متطلبات جديدة:

"أليس، أريد تغيير نص الزر من 'إضافة إلى سلة التسوق' إلى 'إضافة إلى الحقيبة'. كما يجب أن تعرض البطاقة status التوفر. ويجب أن يتكيف التصميم مع أجهزة الجوال."

يتعين على أليس تعديل أكثر من 30 ملف HTML ثلاث مرات = 90 تعديلاً. ومن المستحيل الحفاظ على هذا الوضع.

(2) حل مكونات Vue: ملف ProductCard.vue واحد، أعيد استخدامه 30 مرة

VUE
<!-- components/ProductCard.vue - 1 Component Definitions -->
<template>
  <div class="product-card">
    <img :src="product.image">
    <h3>{{ product.name }}</h3>
    <p>${{ product.price }}</p>
    <button :disabled="product.stock === 0" @click="addToCart">
      {{ product.stock === 0 ? 'Out of Stock' : 'Add to Bag' }}
    </button>
  </div>
</template>

<script setup>
const props = defineProps({ product: Object })
const emit = defineEmits(['add-to-cart'])

function addToCart() {
  emit('add-to-cart', props.product.id)
}
</script>
VUE
<!-- HomeView.vue / ProductList.vue / SearchResults.vue -->
<template>
  <ProductCard v-for="product in products" :key="product.id" :product="product" @add-to-cart="handleAdd" />
</template>

1 ProductCard.vue → أُعيد استخدامه 30 مرة. لتغيير نص الزر، ما عليك سوى إجراء التغيير في مكان واحد فقط.

(3) الإيرادات

بعد تقسيم النظام إلى مكونات:



3. ما المقصود بالمكون؟

(1) التعريف

المكون في Vue هو وحدة واجهة مستخدم قابلة لإعادة الاستخدام لها منطقها الخاص وأسلوبها التصميمي. يتكون كل مكون من مثيل واحد لـ Vue بالإضافة إلى بياناته وأساليبه ودورة حياته الخاصة.

100%
graph TB
    A[App.vue<br/>Root Component] --> B[HomeView.vue<br/>Home]
    A --> C[AboutView.vue<br/>About]
    A --> D[DashboardView.vue<br/>Dashboard]
    
    B --> E[ProductCard.vue<br/>Product Card]
    B --> F[ProductList.vue<br/>Product List]
    B --> G[FilterBar.vue<br/>Filter Bar]
    
    E --> H[Button.vue<br/>Button]
    E --> I[Badge.vue<br/>Badge]
    
    F --> E
    G --> J[Select.vue<br/>Scroll down]
    G --> K[Input.vue<br/>Input]
    
    style A fill:#42b883,color:#fff
    style E fill:#42b883,color:#fff
    style H fill:#42b883,color:#fff

(2) 5 ميزات رئيسية

الميزة الوصف
قابل لإعادة الاستخدام يمكن استخدام مكون واحد في N أماكن
التغليف فصل البيانات والأساليب والأسلوب بحيث لا تتداخل فيما بينها
نظام المكونات مكونات أصغر تُجمع معًا لتشكيل مكونات أكبر
قابل للاختبار اختبار الوحدة لمكون واحد
قابلية الصيانة تغيير مكان واحد = تغيير N أماكن

(3) المكونات مقابل الوظائف

البعد الوظيفة المكون
المكونات القابلة لإعادة الاستخدام منطق البرمجة واجهة المستخدم + المنطق
المدخلات الحجج (args) الخصائص (props)
الإخراج الإرجاع الإرسال
الstatus المتغيرات المحلية البيانات / المراجع
الآثار الجانبية لا شيء ربطات دورة الحياة


4. مكون ملف واحد (SFC)

(1) ما هو SFC؟

SFC (مكون ملف واحد) = ملف .vue واحد = مكون واحد. إن دمج الأقسام template وscript وstyle في ملف واحد يسهل عملية إدارتها.

VUE
<!-- ProductCard.vue - SFC Single-File Components -->
<template>
  <!-- 1. template:HTML Template(Required) -->
  <div class="product-card">
    <h3>{{ product.name }}</h3>
    <p>${{ product.price }}</p>
  </div>
</template>

<script setup>
// 2. script:JS Logic(Required)
import { ref } from 'vue'

const props = defineProps({ product: Object })
const count = ref(0)
</script>

<style scoped>
/* 3. style:CSS Style(Optional) */
.product-card {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 1rem;
}
</style>

(2) شرح مفصل للجزء الثالث من قانون الأوراق المالية والبورصات (SFC)

القسم الوظيفة هل هي مطلوبة؟ اللغة
<template> قالب HTML أمر HTML + Vue
<script setup> JS Logic JavaScript / TypeScript
<style scoped> أنماط CSS CSS / SCSS / Less

(3) سلسلة SFC مقابل سلسلة HTML

JS
// ❌ No need SFC(String Templates,Vue 2 Style)
Vue.component('my-component', {
  template: '<div>{{ msg }}</div>',
  data() { return { msg: 'Hello' } }
})

// ✅ Use SFC (Recommended)
// MyComponent.vue
<template>
  <div>{{ msg }}</div>
</template>
<script setup>
const msg = 'Hello'
</script>

مزايا SFC:



5. تسجيل المكونات

(1) التسجيل الشامل (لا يُنصح به للمشاريع الكبيرة)

JS
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import ProductCard from './components/ProductCard.vue'

const app = createApp(App)

// Global Registration:All components are available <ProductCard />
app.component('ProductCard', ProductCard)

app.mount('#app')

(2) التسجيل المحلي (موصى به)

VUE
<!-- HomeView.vue -->
<script setup>
// 1. Import
import ProductCard from '@/components/ProductCard.vue'

// 2. Use in a template(No registration required,Automatically Available)
</script>

<template>
  <ProductCard :product="product" />
</template>

(3) مقارنة بين طريقتي التسجيل

البعد التسجيل العالمي التسجيل المحلي
النطاق التطبيق بأكمله المكون الحالي
موقع الرمز main.js داخل المكون <script setup>
تغيير هيكل الشجرة ❌ غير مدعوم ✅ مدعوم
تلميحات الأنواع ضعيفة قوية (الإكمال التلقائي في IDE)
التوصية ⭐⭐ للمكونات الإضافية ⭐⭐⭐⭐⭐ للاستخدام التجاري


6. قواعد تسمية المكونات

(1) 3 قواعد تسمية

النمط المثال الاستخدام التوصية
PascalCase ProductCard.vue استيراد JS / في القوالب ⭐⭐⭐⭐⭐
kebab-case product-card.vue أسماء الملفات / قوالب kebab-case ⭐⭐⭐
camelCase productCard.vue غير موصى به

(2) الاستخدام في القوالب

VUE
<template>
  <!-- PascalCase(Recommendations,IDE Friendly) -->
  <ProductCard />
  <UserProfile />
  
  <!-- kebab-case(Also supported) -->
  <product-card />
  <user-profile />
  
  <!-- ✅ Either one works.,PascalCase Easier to read -->
</template>

(3) هيكل المجلدات

TEXT 📖 للعرض فقط
src/
├-- components/          # Public Components(For use across pages)
│   ├-- ProductCard.vue
│   ├-- Button.vue
│   └-- Header.vue
├-- views/               # Page Components(For routing)
│   ├-- HomeView.vue
│   ├-- ProductListView.vue
│   └-- DashboardView.vue
├-- composables/         # Composite Functions
│   ├-- useAuth.ts
│   └-- useFetch.ts
├-- router/              # Router Configuration
└-- App.vue              # Root Component


7. أساسيات props و emit

(1) العناصر المساعدة: تنتقل من العنصر الأصلي إلى العنصر التابع

VUE
<!-- Parent Component Parent.vue -->
<template>
  <ProductCard :product="myProduct" :show-stock="true" />
</template>

<!-- Child component ProductCard.vue -->
<template>
  <div>
    <h3>{{ product.name }}</h3>
    <p v-if="showStock">Stock: {{ product.stock }}</p>
  </div>
</template>

<script setup>
// Receive the data passed from the parent component props
const props = defineProps({
  product: { type: Object, required: true },
  showStock: { type: Boolean, default: false }
})
</script>

(2) الإرسال: من العنصر الفرعي إلى العنصر الأصلي

VUE
<!-- Child component ProductCard.vue -->
<template>
  <button @click="handleAdd">Add to Cart</button>
</template>

<script setup>
const emit = defineEmits(['add-to-cart'])

function handleAdd() {
  emit('add-to-cart', { id: 1, name: 'iPhone' })
}
</script>
VUE
<!-- Parent Component Parent.vue -->
<template>
  <ProductCard @add-to-cart="handleAddToCart" />
</template>

<script setup>
function handleAddToCart(product) {
  console.log('Add to cart:', product)
}
</script>

(3) دليل مرجعي سريع: props مقابل emit

الفئة واجهة برمجة التطبيقات (API) الغرض
الأصل → الفرع defineProps() نقل البيانات
الطفل → الوالد defineEmits() إخطار بالحدث


8. مثال كامل: إعادة استخدام 5 بطاقات منتج

▶ مثال: 1. مكون ProductCard.vue المكون من ملف واحد

VUE 📖 للعرض فقط
<!-- src/components/ProductCard.vue -->
<template>
  <div :class="['product-card', { 'out-of-stock': product.stock === 0 }]">
    <img :src="product.image" :alt="product.name">
    <h3>{{ product.name }}</h3>
    <p class="price">${{ product.price }}</p>
    <span class="stock" v-if="product.stock < 10 && product.stock > 0">
      Only {{ product.stock }} left
    </span>
    <button 
      :disabled="product.stock === 0" 
      @click="handleAddToCart"
    >
      {{ product.stock === 0 ? 'Out of Stock' : 'Add to Cart' }}
    </button>
  </div>
</template>

<script setup>
const props = defineProps({
  product: { 
    type: Object, 
    required: true,
    validator: (val) => val.id && val.name && val.price
  }
})

const emit = defineEmits(['add-to-cart'])

function handleAddToCart() {
  if (props.product.stock > 0) {
    emit('add-to-cart', props.product.id)
  }
}
</script>

<style scoped>
.product-card {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 1rem;
  transition: box-shadow 0.2s;
}
.product-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.out-of-stock {
  opacity: 0.6;
}
.price {
  color: #42b883;
  font-weight: bold;
}
button:disabled {
  background: #ccc;
  cursor: not-allowed;
}
</style>
53 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

▶ مثال: 2. إعادة استخدام «ProductCard» في 3 صفحات

VUE
<!-- src/views/HomeView.vue -->
<template>
  <h1>Featured Products</h1>
  <div class="grid">
    <ProductCard 
      v-for="product in featured" 
      :key="product.id" 
      :product="product"
      @add-to-cart="handleAdd"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import ProductCard from '@/components/ProductCard.vue'
import { useCart } from '@/composables/useCart'

const featured = ref([
  { id: 1, name: 'iPhone 15 Pro', price: 999, stock: 50, image: 'iphone.jpg' },
  { id: 2, name: 'MacBook Pro', price: 2499, stock: 20, image: 'macbook.jpg' }
])

const { addToCart } = useCart()
function handleAdd(productId) {
  addToCart(productId)
}
</script>
▶ جرّب الكود
VUE
<!-- src/views/ProductListView.vue -->
<template>
  <ProductCard 
    v-for="product in products" 
    :key="product.id" 
    :product="product"
    @add-to-cart="handleAdd"
  />
</template>

<script setup>
import ProductCard from '@/components/ProductCard.vue'
// ... Exactly the same citation,Reusable Components
</script>

▶ مثال: 3. مقارنة بين التسجيل العالمي والتسجيل المحلي

JS
// ❌ main.js Global Registration(Not recommended)
import ProductCard from './components/ProductCard.vue'
app.component('ProductCard', ProductCard)  // The entire app is available

// ✅ HomeView.vue Local Registration(Recommendations)
<script setup>
import ProductCard from '@/components/ProductCard.vue'
// Only at HomeView.vue Available in China
</script>
▶ جرّب الكود

▶ مثال: 4. الأنواع الخمسة الرئيسية للأدوات المسرحية

VUE
<script setup>
defineProps({
  // Basic Types
  name: String,
  age: Number,
  active: Boolean,
  
  // Complex Types
  user: Object,
  items: Array,
  
  // Required + Default value
  title: { type: String, required: true },
  pageSize: { type: Number, default: 20 },
  
  // Validator
  email: { 
    type: String, 
    validator: (val) => val.includes('@') 
  },
  
  // Custom Types
  status: { 
    type: String, 
    validator: (val) => ['active', 'inactive'].includes(val) 
  }
})
</script>
▶ جرّب الكود

▶ مثال: 5. 5 مبادئ تصميم المكونات

المبدأ الوصف
المسؤولية الواحدة كل مكون يقوم بمهمة واحدة
قابل لإعادة الاستخدام واجهة مستخدم مجردة وعامة (أزرار/حقول إدخال/نوافذ منبثقة)
قابل للتكوين تتحكم الخصائص (props) في السلوك، بينما تتحكم الفتحات (slots) في المحتوى
نظام المكونات مكونات أصغر تُجمع معًا لتشكيل مكونات أكبر
سهولة الاختبار المكونات المستقلة تجعل اختبار الوحدات أمرًا سهلاً

▶ مثال: 6. 5 أخطاء شائعة

الخطأ الأعراض الحل
ملف قائمة المكونات خطأ في بيئة التطوير المتكاملة استخدم أسلوب PascalCase: ProductCard.vue
عدد كبير جدًا من التسجيلات العالمية حجم حزمة أكبر التسجيلات المحلية تدعم ميزة «tree-shaking»
المكون كبير جدًّا يصعب صيانته قسّمه إلى مكونات أصغر
تعديل الخصائص مباشرةً تحذير من Vue الخصائص مخصصة للقراءة فقط؛ استخدم emit بدلاً من ذلك
نسيت استيراد مكون القالب يعرض عبارة "مكون غير معروف" تحقق من عملية الاستيراد في <script setup>


❓ أسئلة شائعة

س متى ينبغي استخدام التسجيل العام؟
ج في حالتين فقط: (1) مكونات المكونات الإضافية (مثل <el-button> من Element Plus)؛ (2) المكونات الأساسية المستخدمة في جميع أنحاء التطبيق (الأزرار/الأيقونات). أما المكونات التجارية فيجب تسجيلها محليًّا دائمًا حتى تصبح عملية «تصفية الشجرة» سارية المفعول.
س هل يجب أن يتضمن SFC الأجزاء الثلاثة جميعها؟
ج <template> و<script> مطلوبان؛ أما <style> فهو اختياري. إذا كان المكون يتألف من لغة JavaScript فقط (على سبيل المثال، إذا كان مخصصًا لتغليف البيانات فقط)، فيمكن حذف القالب (باستخدام الدالة render) بدلاً من ذلك.
س ما هو الحجم المناسب لملف المكون؟
ج نوصي بأن لا يتجاوز عدد الأسطر فيه 200 سطر. إذا تجاوز عدد الأسطر 300 سطر، ففكر في تقسيمه. فكلما كان المكون أبسط، زادت سهولة صيانته.
س يستخدم SFC الامتداد .vue. كيف تتعرف عليه الأدوات الأخرى؟
ج تتعرف كل من Vite وWebpack وVue CLI على الامتداد .vue بشكل افتراضي. أما بالنسبة لـ VS Code، فقم بتثبيت ملحق "Vue - Official".
س كيف يمكنني اختبار مكون واحد؟
ج استخدم Vitest + Vue Test Utils (@vue/test-utils): mount(ProductCard, { props: { product: mockProduct } }). تتناول المرحلة 4.6 من هذا الدليل هذا الموضوع بالتفصيل.
س كيف يمكنني استخدام مكتبة المكونات (Element Plus)؟
ج قم بالتثبيت عبر npm → قم بالتسجيل عالميًا في ملف main.js → استخدم <el-button> مباشرةً في قوالبك. لمزيد من التفاصيل، راجع الفصل 4.7 من Phase.
س ما الفرق بين SFC و React JSX؟
ج تستخدم SFC قوالب شبيهة بـ HTML (قريبة من معايير الويب)، بينما تستخدم React JSX (كتابة HTML باستخدام JavaScript). تتميز SFC بسهولة التعلم، في حين أن JSX تتميز بالمرونة ولكنها تتطلب خطوة بناء. يدعم Vue 3 أيضًا JSX (@vitejs/plugin-vue-jsx)، لكن SFC هو الخيار الموصى به افتراضيًا.

📖 ملخص


📝 تمارين

  1. أسئلة أساسية (مستوى الصعوبة: ⭐)

    كتابة مكون زر src/components/BaseButton.vue:

    • المعلمات: text (سلسلة)، variant (سلسلة، 'primary'/'success'/'danger')
    • هناك 3 أنواع تتوافق مع 3 فئات
    • إصدار حدث click
    • استخدم 3 أشكال مختلفة في ملف HomeView.vue
  2. مسائل متقدمة (مستوى الصعوبة: ⭐⭐)

    إنشاء قائمة منتجات + نظام البطاقات:

    • ProductCard.vue: بطاقة منتج واحد (الخصائص: product، الإشارة: add-to-cart)
    • ProductList.vue: حاوية قائمة المنتجات (v-for + التصفية)
    • App.vue: المكون الجذري؛ يقوم بتمرير البيانات والتعامل مع حدث "الإضافة إلى سلة التسوق"
    • 3 ملفات و3 مكونات مستخدمة في بنية متداخلة
  3. مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)

    تنفيذ نظام مكونات «بطاقة المستخدم» متكامل:

    1. Avatar.vue: مكون الصورة الرمزية (يدعم كل من وضع عنوان URL ووضع الأحرف الأولى)
    2. UserCard.vue: بطاقة المستخدم (مع الصورة الرمزية)
    3. UserList.vue: قائمة المستخدمين (باستخدام UserCard)
    4. App.vue: المكون الجذري (البيانات + التفاعل)
    5. 5 خصائص (الصورة الرمزية/الاسم/البريد الإلكتروني/الدور/status النشاط)
    6. 3 أحداث (تحرير/حذف/تحديد)
    7. عزل الأنماط المحددة النطاق
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%