Vue.js: دورة حياة المكون
آخر تحديث: 2026-08-26
دورة حياة المكون هي العملية الكاملة التي يمر بها مكون Vue من الإنشاء إلى الإزالة: إنشاء مثيل → التثبيت في DOM → تحديثات البيانات → إلغاء التثبيت. توفر Vue ثمانية ربطات لدورة الحياة تتيح لك تنفيذ التعليمات البرمجية في أوقات محددة.
يُعد فهم دورة الحياة أمرًا أساسيًّا لكتابة مكونات «حية» — حيث يمكنك تحميل البيانات عند التثبيت، وتنفيذ الآثار الجانبية عند التحديث، وتنظيف الموارد عند إلغاء التثبيت.
1. ما ستتعلمه
- مخطط تدفق كامل لدورة حياة المكون، بدءًا من إنشائه وحتى إزالته
- 8 نقاط ربط في دورة الحياة (onBeforeMount، onMounted، إلخ)
- قواعد بناء جملة واجهة برمجة التطبيقات (Composition API) (قواعد تسمية onX)
- أفضل حالات الاستخدام لكل «هوك»
- ترتيب تنفيذ دورات حياة المكونات الأم والفرعية
- الحفاظ على المكونات المخزنة مؤقتًا (مفعَّلة / معطلة)
- معالجة الأخطاء onErrorCaptured
2. مشكلة «الوميض» في أحد مكونات تحميل البيانات
(1) المشكلة: تم عرض المكون، لكن البيانات لم تُحمَّل بعد
قامت أليس بإنشاء مكون لملف تعريف المستخدم:
<!-- ❌ The "Broken" Version:Rendering Before Data Arrives -->
<template>
<div>
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const user = ref({}) // Empty object,Display during rendering "undefined"
fetch('/api/user')
.then(res => res.json())
.then(data => user.value = data)
</script>
تجربة المستخدم:
- 0 مللي ثانية: تعرض الصفحة «undefined undefined» (0.5 ثانية)
- 500 مللي ثانية: وصول البيانات؛ تظهر الرسالة «Alicealice@example.com»
- تومض لمدة 0.5 ثانية، مما يؤدي إلى تجربة مستخدم سيئة للغاية
مدير المنتج تشارلي:
"أليس، يظهر للمستخدمين عبارة 'غير محدد' لمدة نصف ثانية. نحن بحاجة إلى status تحميل. اعرضي عبارة 'جاري التحميل...' حتى تصل البيانات."
(2) دورة حياة Vue + حل لstatus "التحميل"
<template>
<div>
<p v-if="loading">Loading...</p>
<div v-else>
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const user = ref(nulo)
const loading = ref(true)
// ✅ Load data in onMounted... (DOM Ready, loading status will be displayed first)
onMounted(async () => {
const res = await fetch('/api/user')
user.value = await res.json()
loading.value = false
})
</script>
تجربة المستخدم:
- 0 مللي ثانية: عرض "جاري التحميل..." ✅
- 500 مللي ثانية: وصول البيانات؛ يعرض "Alice alice@example.com" ✅
- خالٍ من الوميض ✅
(3) الإيرادات
بعد إضافة «دورة الحياة»:
- مشكلة الوميض: 100% → 0
- تجربة المستخدم: status التحميل واضحة
- قابلية التوسع: يمكن تطبيق النمط نفسه على جميع مكونات البيانات
3. مخطط تدفق دورة الحياة الكاملة
graph TB
A[Create a component instance] --> B[onBeforeCreate]
B --> C[setup Responsive]
C --> D[onCreated]
D --> E[Template Compilation]
E --> F[onBeforeMount]
F --> G[Mount to DOM]
G --> H[onMounted]
H --> I{Data Update?}
I -->|Yes| J[onBeforeUpdate]
J --> K[Render Again]
K --> L[onUpdated]
L --> I
I -->|No| M{Component Uninstall?}
M -->|Yes| N[onBeforeUnmount]
N --> O[Unmount]
O --> P[onUnmounted]
style H fill:#42b883,color:#fff
style L fill:#42b883,color:#fff
style P fill:#42b883,color:#fff
4. شرح مفصل لـ 8 نقاط ربط في دورة الحياة
(1) دليل مرجعي سريع للخطافات الثمانية الرئيسية
| العنصر | شروط التشغيل | السيناريوهات الشائعة | التكرار |
|---|---|---|---|
onBeforeMount |
قبل تركيب المكونات | إعداد البيانات | ⭐ |
onMounted |
بعد تحميل المكون | تحميل البيانات، عمليات DOM | ⭐⭐⭐⭐⭐ |
onBeforeUpdate |
قبل تحديث البيانات | تحسين الأداء | ⭐⭐ |
onUpdated |
بعد تحديث البيانات | العمليات التي تلي إعادة عرض DOM | ⭐⭐⭐ |
onBeforeUnmount |
قبل تفريغ أحد المكونات | مسح المؤقتات | ⭐⭐⭐ |
onUnmounted |
بعد إلغاء تثبيت المكون | التنظيف النهائي | ⭐⭐ |
onErrorCaptured |
تم رصد خطأ في المكون الفرعي | حدود الخطأ | ⭐⭐⭐ |
onActivated |
تم تمكين ميزة Keep-Alive | تمت استعادة مكون ذاكرة التخزين المؤقت | ⭐⭐ |
onDeactivated |
ميزة «keep-alive» معطلة | مكون التخزين المؤقت معطل | ⭐⭐ |
(2) onMounted: الأكثر استخدامًا
<script setup>
import { ref, onMounted } from 'vue'
const user = ref(null)
onMounted(async () => {
// 1. Loading initial data
const res = await fetch('/api/user')
user.value = await res.json()
// 2. DOM Operations(Realistic operation DOM)
document.title = `${user.value.name} - Dashboard`
// 3. Register a global event listener
window.addEventListener('resize', handleResize)
// 4. Set a Timer
const timer = setInterval(() => {
console.log('Tick')
}, 1000)
// ❌ Note: onUnmounted must clean up timer and listener
})
</script>
(3) onUnmounted: تنظيف الموارد
<script setup>
import { onMounted, onUnmounted } from 'vue'
let timer = nulo
onMounted(() => {
timer = setInterval(() => console.log('Tick'), 1000)
window.addEventListener('resize', handleResize)
})
// ✅ Cleanup:Avoiding Memory Leaks
onUnmounted(() => {
clearInterval(timer)
window.removeEventListener('resize', handleResize)
})
</script>
(4) onErrorCaptured: حدود الخطأ
<!-- ErrorBoundary.vue Parent Component -->
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
console.error('Caught error:', err)
console.log('Component:', instance)
console.log('Info:', info)
error.value = err.message
return false // Prevent errors from propagating upward
})
</script>
<template>
<div>
<p v-if="error" class="error">Error: {{ error }}</p>
<slot v-else />
</div>
</template>
<!-- App.vue Usage -->
<ErrorBoundary>
<UserProfile :user-id="123" /> <!-- If an error occurs, will be caught by ErrorBoundary -->
</ErrorBoundary>
(5) 5 حالات استخدام رئيسية
| السيناريو | العنصر الجاذب | الكود |
|---|---|---|
| تحميل البيانات الأولية | onMounted |
await fetch(...) |
| ضبط المؤقت | onMounted + onUnmounted |
setInterval + clearInterval |
| تابع آخر الأحداث العالمية | ||
| معالجة DOM | onMounted |
document.querySelector(...) |
| دمج المكتبات الخارجية | onMounted + onUnmounted |
new Chart(...) + chart.destroy() |
5. ترتيب تنفيذ دورات حياة المكونات الأم والمكونات التابعة
(1) ترتيب التثبيت
sequenceDiagram
participant P as Parent
participant C as Child
P->>P: 1. Parent onBeforeMount
P->>P: 2. Parent onMounted
C->>C: 3. Child onBeforeMount
C->>C: 4. Child onMounted
Note over P,C: ❌ That's wrong: Parent mounted should come after Child
الترتيب الصحيح:
sequenceDiagram
participant P as Parent
participant C as Child
P->>P: 1. Parent onBeforeMount
C->>C: 2. Child onBeforeMount
C->>C: 3. Child onMounted
P->>P: 4. Parent onMounted
Note over P,C: ✅ Mount only after all child components, including the parent component, have finished mounting
(2) أمر التنفيذ الكامل (المكونات المتداخلة)
1. Parent onBeforeCreate
2. Parent setup
3. Parent onCreated
4. Parent onBeforeMount
5. Child onBeforeCreate
6. Child setup
7. Child onCreated
8. Child onBeforeMount
9. Child onMounted
10. Parent onMounted
(3) ترتيب إلغاء التثبيت (بالترتيب العكسي)
1. Parent onBeforeUnmount
2. Child onBeforeUnmount
3. Child onUnmounted
4. Parent onUnmounted
6. مكون التخزين المؤقت «Keep-Alive»
(1) ما المقصود بـ «keep-alive»؟
<keep-alive> يقوم بتخزين مثيلات المكونات مؤقتًا لتجنب تكرار إنشائها وإزالتها. ويُستخدم عادةً في الحالات التي تنطوي على التبديل بين علامات التبويب والتبديل بين المسارات.
<!-- Parent Component -->
<template>
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">
{{ tab }}
</button>
<!-- keep-alive Package:Switch Tab Do not destroy the component -->
<keep-alive>
<component :is="currentTabComponent" />
</keep-alive>
</template>
(2) خطافان جديدان
<!-- Child component: Cached by keep-alive -->
<script setup>
import { onActivated, onDeactivated } from 'vue'
// When the component is activated(Restore from Cache)
onActivated(() => {
console.log('Component activated')
// Reload Data,Restore scroll position, etc.
})
// When a component is disabled(Cut but keep in cache)
onDeactivated(() => {
console.log('Component deactivated')
// Save Status,Pause the timer, etc.
})
</script>
(3) 5 حالات استخدام رئيسية
| السيناريو | الطريقة |
|---|---|
| التبديل بين علامات التبويب | <keep-alive> لف محتوى tag التبويب |
| تبديل المسار | حزمة <keep-alive> <router-view> |
| ذاكرة التخزين المؤقتة للنوافذ المنبثقة | <keep-alive> نافذة منبثقة للحزمة |
| صفحة القوائم | القوائم المخزنة مؤقتًا التي تم تحميلها |
| صفحة النموذج | تخزين النماذج التي لم يتم إرسالها مؤقتًا |
7. مثال كامل: مكون ملف تعريف المستخدم
▶ مثال: 1. onMounted + onUnmounted (تحميل البيانات + التنظيف)
<template>
<div>
<p v-if="loading">Loading...</p>
<div v-else-if="user">
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const props = defineProps({ userId: { type: Number, required: true } })
const user = ref(null)
const loading = ref(true)
let timer = null
onMounted(async () => {
// 1. Loading data
const res = await fetch(`/api/users/${props.userId}`)
user.value = await res.json()
loading.value = false
// 2. Set a Timer (Every 30s Refresh)
timer = setInterval(async () => {
const res = await fetch(`/api/users/${props.userId}`)
user.value = await res.json()
}, 30000)
// 3. Monitoring Window Size Changes
window.addEventListener('resize', () => {
console.log('Window resized')
})
})
onUnmounted(() => {
// ✅ Cleanup:Avoiding Memory Leaks
clearInterval(timer)
window.removeEventListener('resize', () => {})
})
</script>
▶ مثال: 2. مرجع سريع لـ 8 خطافات
<script setup>
import {
onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted,
onErrorCaptured, onActivated, onDeactivated
} from 'vue'
// 1. Before Mounting
onBeforeMount(() => {
console.log('1. Preparing to mount')
})
// 2. After mounting(Most Commonly Used)
onMounted(() => {
console.log('2. Mounted,Loading data')
})
// 3. Before the update
onBeforeUpdate(() => {
console.log('3. Coming Soon')
})
// 4. After the update
onUpdated(() => {
console.log('4. Updated')
})
// 5. Before Uninstalling
onBeforeUnmount(() => {
console.log('5. Preparing to uninstall,Free Up Resources')
})
// 6. After uninstallation
onUnmounted(() => {
console.log('6. Uninstalled')
})
// 7. Error Handling
onErrorCaptured((err) => {
console.erro('7. Child Component Error:', err)
return false
})
// 8. keep-alive Activate
onActivated(() => {
console.log('8. keep-alive Activate')
})
// 9. keep-alive Disable
onDeactivated(() => {
console.log('9. keep-alive Disable')
})
</script>
▶ مثال: 3. ترتيب دورة حياة المكونات الأم والمكونات التابعة
<!-- Parent.vue -->
<template>
<Child :data="parentData" />
</template>
<script setup>
import { onMounted } from 'vue'
onMounted(() => console.log('4. Parent mounted'))
</script>
<!-- Child.vue -->
<template>
<p>{{ data }}</p>
</template>
<script setup>
import { onMounted } from 'vue'
onMounted(() => console.log('3. Child mounted'))
</script>
<!-- Order of Console Output:
1. Child created
2. Child mounted
3. Parent mounted
(Because the parent component must wait until the child component has finished mounting before it can mount itself) -->
▶ مثال: 4. حدود الخطأ onErrorCaptured
<!-- ErrorBoundary.vue -->
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
console.error('Caught error:', err)
console.log('Component:', instance?.$options.name)
console.log('Info:', info) // 'render' / 'watch' / 'lifecycle hook'
error.value = err.message
return false // Prevent upward transmission
})
</script>
<template>
<div v-if="error" class="error-banner">
⚠️ Error: {{ error }}
</div>
<slot v-else />
</template>
▶ مثال: 5. مكون التخزين المؤقت لـ keep-alive
<!-- TabContainer.vue -->
<template>
<div classe="tabs">
<button
v-for="tab in tabs"
:key="tab"
:classe="{ active: currentTab === tab }"
@click="currentTab = tab"
>
{{ tab }}
</button>
<!-- keep-alive Cache: Preserve component state when switching away -->
<keep-alive>
<component :is="currentTabComponent" />
</keep-alive>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import Home from './tabs/Home.vue'
import Profile from './tabs/Profile.vue'
import Settings from './tabs/Settings.vue'
const tabs = ['Home', 'Profile', 'Settings']
const currentTab = ref('Home')
const currentTabComponent = computed(() => {
return { Home, Profile, Settings }[currentTab.value]
})
</script>
▶ مثال: 6. مرجع سريع لـ 5 أخطاء شائعة
| الخطأ | الأعراض | الحل |
|---|---|---|
استخدم document في المستوى الأعلى من setup |
خطأ في الخادم | استخدم onMounted |
| لم يتم مسح المؤقت | تسرب في الذاكرة | مسح في onUnmounted |
| تعديل المتغيرات في onMounted | تحذير Vue | استخدم emit بدلاً من ذلك |
| المكونات غير المتزامنة لا تتعامل مع الأخطاء | الفشل الصامت | onErrorCaptured |
| الترتيب الخاطئ لتركيب العناصر الأصلية والتابعة | توضيح الالتباس في تصحيح الأخطاء | فهم مبدأ «العنصر الأصلي يساوي العنصر التابع» |
❓ أسئلة شائعة
onMounted وonCreated؟onCreated، يكون المكون قد تم إنشاؤه ولكن لم يتم عرض DOM بعد (لذلك لا يمكنك التعامل مع DOM). وعند استدعاء onMounted، يكون DOM قد تم تثبيته (لذلك يمكنك التعامل مع DOM). يتم التعامل مع تحميل البيانات عمومًا في onMounted (لتجنب مشكلات SSR).onMounted(async () => { await fetch(...) }) هي صيغة صحيحة. لا تنتظر Vue اكتمال العملية غير المتزامنة قبل المتابعة.onActivated وonMounted في keep-alive؟onMounted مرة واحدة فقط (عند التثبيت الأولي). أما onActivated فيتم تشغيله في كل مرة يتم فيها استعادة العرض من ذاكرة التخزين المؤقت. ويؤدي التبديل بين علامات التبويب إلى تشغيل onActivated وonDeactivated (وليس onMounted وonUnmounted).onErrorCaptured رصدها؟@hook:mounted="handleChildMounted". ومع ذلك، يُنصح باستخدام props/emit للتواصل بدلاً من الاستماع مباشرةً إلى أحداث دورة الحياة.onMounted (كان mounted في Vue 2) وأضافت onErrorCaptured و onActivated/onDeactivated. جميع خطافات Vue 2 متوفرة في Vue 3 (تم الحفاظ على التوافق مع الإصدارات السابقة).📖 ملخص
- 8 نقاط ربط لدورة الحياة: onBeforeMount / onMounted / onBeforeUpdate / onUpdated / onBeforeUnmount / onUnmounted / onErrorCaptured / onActivated
- onMounted (الأكثر استخدامًا): معالجة DOM، وتحميل البيانات، وتسجيل الأحداث
- onUnmounted: عمليات التنظيف الإلزامية: المؤقتات، ومستمعو الأحداث، ومكتبات الجهات الخارجية
- تسلسل الأوامر بين العنصر الأصلي والعنصر التابع: تنفيذ الأمر «beforeMount» للعنصر الأصلي → تنفيذ الأمر «beforeMount» للعنصر التابع → تثبيت العنصر التابع → تثبيت العنصر الأصلي
- keep-alive + onActivated/onDeactivated للمكونات المخزنة مؤقتًا
- onErrorCaptured: تنفيذ حدود الخطأ
- لا يتم تشغيل بعض العناصر المرتبطة أثناء العرض من جانب الخادم
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
تنفيذ مكون UserCard بسيط:
- onMounted: تحميل بيانات المستخدم (محاكاة تأخير مدته ثانية واحدة)
- إظهار «جاري التحميل» → إظهار معلومات المستخدم
- سجلات onUnmounted: "تم إتلاف المكون"
-
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
تنفيذ مكون «المؤقت»:
- onMounted: بدء تشغيل مؤقت (+1 في الثانية)
- عرض الثانية الحالية
- onUnmounted: مسح المؤقت
- إضافة زر للإيقاف المؤقت/الاستئناف (استخدم
refلتخزين الstatus)
-
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
تنفيذ نظام علامات تبويب كامل:
- TabContainer.vue: 3 علامات تبويب (الصفحة الرئيسية/الملف الشخصي/الإعدادات)
- 3 مكونات لمحتوى علامات التبويب
- استخدم مكون التخزين المؤقت
<keep-alive> - تبديل tag التبويب «X نشط، Y غير نشط»
- قم بتحميل البيانات في إحدى علامات التبويب باستخدام
onMounted - لا يتم إغلاق الاتصال عند الانتقال إلى تطبيق آخر (بسبب ميزة «keep-alive»)، لذا تظل البيانات موجودة عند العودة إلى التطبيق الأصلي
- استخدم
onErrorCapturedلتحديد الأخطاء في المكونات الفرعية