المكونات
المكونات في فيو جي إس (Vue.js) هي اللبنات الأساسية لبناء واجهات مستخدم قابلة لإعادة الاستخدام، وتعزل المنطق، والحالة، والعرض في وحدات مستقلة. أهمية المكونات تنبع من قدرتها على فصل الاهتمامات (separation of concerns)، تمكين الاختبار، وتسهيل التعاون في فرق تطوير واسعة النطاق. تُستخدم المكونات عندما نحتاج إلى تجزئة واجهة معقدة إلى أجزاء أصغر قابلة للصيانة — مثل عناصر الإدخال المعقدة، لوحات البيانات، أو وحدات إدارة الحالة المحلية. في هذه الوحدة سنعالج تركيب المكونات باستخدام Vue 3 (بافتراض استخدام Composition API و<script setup> كافتراضي حديث)، مع التركيز على بناء مكونات متسلسلة، تمرير props وemits، استخدام provide/inject وcomposables لإدارة اللوجيك المعقد، وتطبيق أنماط OOP داخل نماذج البيانات حيث يكون ذلك مناسبًا.
سوف نتناول تركيب الجمل (syntax) الصحيح، هياكل البيانات الشائعة (reactive, ref, computed, Map/Set عند الحاجة)، الخوارزميات المستخدمة لتنقية القوائم أو فرزها بكفاءة، ومبادئ OOP لتصميم نماذج بيانات قابلة للتوسع. سيتعلم القارئ كيفية تجنب مشاكل شائعة مثل تسريبات الذاكرة عند طلبات الشبكة غير الملغاة، كيفية تنفيذ المعالجة الآمنة للأخطاء، وتحسين الأداء عبر lazy-loading وmemoization والـvirtualization عند عرض قوائم كبيرة. سياق المحتوى مرتبط بهندسة الأنظمة: المكونات كعناصر في طبقة العرض ضمن تطبيقات ذات معماريات مثل micro-frontends وcomponent-driven design.
مثال أساسي
text// Create a simple, functional فيو جي إس (Vue.js) example demonstrating المكونات
// This example is built for Vue 3 with <script setup> and runs in a Vite project
// Focus: syntax, data structures, reactive state, props, emits, lifecycle cleanup
// File: src/components/TodoItem.vue <template>
<li class="todo-item">
<input type="checkbox" :checked="todo.completed" @change="toggle" />
<span :class="{ done: todo.completed }">{{ todo.title }}</span>
<button @click="remove">حذف</button>
</li>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
todo: { type: Object, required: true } // { id, title, completed }
})
const emit = defineEmits(['toggle', 'remove'])
function toggle() {
emit('toggle', props.todo.id)
}
function remove() {
emit('remove', props.todo.id)
}
</script>
<style scoped>
.todo-item { display:flex; gap:8px; align-items:center; }
.done { text-decoration: line-through; color: #888; }
</style>
// File: src/App.vue <template>
<main>
<h1>قائمة المهام</h1>
<form @submit.prevent="addTodo">
<input v-model="newTitle" placeholder="أضف مهمة" />
<button type="submit">إضافة</button>
</form>
<ul>
<TodoItem
v-for="t in todos"
:key="t.id"
:todo="t"
@toggle="onToggle"
@remove="onRemove"
/>
</ul>
</main>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
import TodoItem from './components/TodoItem.vue'
// بيانات تفاعلية باستخدام هياكل مناسبة
const todos = ref([
{ id: 1, title: 'تعلم المكونات', completed: false },
{ id: 2, title: 'كتابة اختبار', completed: false }
])
const newTitle = ref('')
// وظائف آمنة وخالية من تسريبات الذاكرة
function addTodo() {
const title = newTitle.value && newTitle.value.trim()
if (!title) return
const id = Date.now()
todos.value.push({ id, title, completed: false })
newTitle.value = ''
}
function onToggle(id) {
const item = todos.value.find(t => t.id === id)
if (item) item.completed = !item.completed
}
function onRemove(id) {
const idx = todos.value.findIndex(t => t.id === id)
if (idx !== -1) todos.value.splice(idx, 1)
}
</script>
<style>
main { max-width:640px; margin:1rem auto; font-family:system-ui; }
form { display:flex; gap:8px; margin-bottom:12px; }
</style>
شرح المثال الأساسي أعلاه: يبدأ التصميم بفصل المسؤوليات — مكوّن TodoItem مسؤول عن عرض عنصر واحد واستقبال أحداث التبديل والحذف عبر emits، بينما App.vue يدير حالة القائمة كـ ref من مصفوفة. استخدام Composition API و<script setup> يعطي كتابة أكثر اختصارًا وأداءً أفضل في وقت البناء. قمنا بتعريف props صارمًا لتقليل الأخطاء، وdefineEmits لتوثيق الأحداث الصادرة. هياكل البيانات: نستخدم ref للقائمة لأننا نحتاج إلى إعادة تعيين المرجع عند التعديل؛ داخل العناصر نفسها نستخدم كائنات بسيطة { id, title, completed } لسهولة البحث والتعديل.
من منظور الخوارزميات والأداء، تخصيص البحث بإيجاد العنصر عبر find أو findIndex مناسب لقوائم صغيرة؛ لقوائم كبيرة يجب تبديل الخوارزمية إلى خريطة Map أو استخدام keyed rendering مع lazy-loading. لتجنب تسريبات الذاكرة عند عمليات الشبكة (غير موجودة هنا لكن مهمة متقدمة)، نستخدم دائمًا آليات الإلغاء مثل AbortController في composables أو نلغي المشاهدات في onBeforeUnmount. معالجة الأخطاء في هذا المثال بسيطة — التحقق من المدخلات قبل الإضافة — لكن في حالات فعلية استعمل try/catch حول الوعود (async/await) وأظهر رسائل خطأ للمستخدم دون كسر الحالة.
مثال عملي
text// More advanced فيو جي إس (Vue.js) example building on the previous one
// Real-world: قائمة مهام متزامنة مع API مزيف، يستخدم composable لإدارة الطلبات، OOP model، وملفات مكوّنات متعددة.
// Shows algorithms (debounce, memoization), OOP (class TodoModel), provide/inject, and cleanup via AbortController.
// File: src/models/TodoModel.js
export default class TodoModel {
constructor({ id, title, completed = false }) {
this.id = id
this.title = title
this.completed = completed
}
toggle() { this.completed = !this.completed }
toJSON() { return { id: this.id, title: this.title, completed: this.completed } }
}
// File: src/composables/useTodos.js
import { ref } from 'vue'
export function useTodos() {
const todos = ref([])
let controller = null
async function fetchTodos(signal) {
// simulated fetch - replace with real fetch in production
await new Promise(r => setTimeout(r, 300))
todos.value = [
{ id: 1, title: 'مهمة من الخادم', completed: false }
]
}
async function load() {
if (controller) controller.abort()
controller = new AbortController()
try {
await fetchTodos(controller.signal)
} catch (err) {
if (err.name === 'AbortError') return
console.error('خطأ في جلب البيانات', err)
}
}
function add(title) {
const id = Date.now()
todos.value.push({ id, title, completed: false })
}
function remove(id) {
todos.value = todos.value.filter(t => t.id !== id)
}
function toggle(id) {
const t = todos.value.find(x => x.id === id)
if (t) t.completed = !t.completed
}
function cleanup() {
if (controller) controller.abort()
}
return { todos, load, add, remove, toggle, cleanup }
}
// File: src/AppAdvanced.vue <template>
<section>
<h2>قائمة متقدمة</h2>
<input v-model="q" @input="onSearch" placeholder="بحث (debounced)" />
<button @click="load">إعادة تحميل</button>
<ul>
<li v-for="t in filtered" :key="t.id">
<span>{{ t.title }}</span>
<button @click="toggle(t.id)">تبديل</button>
</li>
</ul>
</section>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useTodos } from './composables/useTodos'
const { todos, load, toggle, cleanup } = useTodos()
onMounted(load)
onBeforeUnmount(cleanup)
// simple debounce implementation
let timer = null
const q = ref('')
function onSearch() {
if (timer) clearTimeout(timer)
timer = setTimeout(() => { /* could call server search */ }, 300)
}
const filtered = computed(() => {
const term = q.value.trim().toLowerCase()
if (!term) return todos.value
return todos.value.filter(t => t.title.toLowerCase().includes(term))
})
</script>
<style scoped>
section { padding:1rem; border:1px solid #eee; border-radius:8px; }
</style>
ممارسات وتحذيرات متقدمة: استخدم composables لفصل منطق البيانات عن العرض — هذا يسهل اختبار الخوارزميات (مثل debounce، memoization، وخوارزميات الفرز/التصفية) وإعادة استخدامها عبر المكونات. عند التعامل مع بيانات من الشبكة، احمِ الطلبات بـ AbortController وألغيها في onBeforeUnmount لتفادي تسريبات الذاكرة. لتسريع البحث عبر قوائم ضخمة استبدل Array.prototype.find وfilter بهياكل سريعة مثل Map أو keyed indexes، أو نفّذ virtual scrolling لعرض العناصر الغير مرئية فقط. بالنسبة للأخطاء، اعمل على طبقات: التحقق المسبق من المدخلات، try/catch على الوعود، وإظهار رسائل صديقة للمستخدم مع تسجيل مفصل للأخطاء للـ DevOps. تجنّب الربط الثنائي غير الضروري عبر props/emit — استخدم نموذج البيانات وحافظ على اتجاه بيانات واحد (one-way data flow) متى أمكن.
تحسينات الأداء: استخدم v-memo أو shallowRef للحد من إعادة التهيئة المكلفة، والـcomputed للتخزين المؤقت للنتائج. راقب heap snapshots عند الاشتباه بتسريب الذاكرة، وضع حدودًا على watchers المكثفة. من الناحية الأمنية، تحقق من أي محتوى HTML خارجي قبل عرضه (sanitize) وتجنّب تمرير دوال غير موثوقة عبر props.
📊 جدول مرجعي
| فيو جي إس (Vue.js) Element/Concept | Description | Usage Example |
|---|---|---|
| props | آلية تمرير البيانات من المكوّن الأب إلى الابن | <Child :value="x" /> |
| emits | إخطار الأب بأحداث من الابن | emit('save', payload) |
| provide/inject | مشاركة القيم بين شجرة المكونات بدون props متتالية | provide('auth', authObj) / inject('auth') |
| composables | دوال قابلة لإعادة الاستخدام لفصل اللوجيك | useTodos(), useAuth() |
| lifecycle hooks | نقاط الارتكاز لإدارة الإنشاء والتنظيف | onMounted(()=>{}), onBeforeUnmount(()=>{}) |
الخلاصة والخطوات التالية: تعلم المكونات يجب أن يركز على تصميم واجهات واضحة وقابلة لإعادة الاستخدام مع مراعاة الأداء والاختبار. الخلاصة: افصل المنطق باستخدام composables، وثّق واجهات المكونات عبر props/emits، واستخدم هياكل بيانات مناسبة (Map للقوائم الكبيرة). الخطوات الموصى بها: دراسة أنماط تصميم المكونات (Container/Presenter، Smart/Dumb components)، التدرّب على virtual scrolling وcode-splitting، وتعميق الفهم في إدارة الحالة المتقدمة (Pinia أو Vuex) وكيفية دمج المكونات في معمارية micro-frontends. نصيحة عملية: ابدأ بمكوّن واحد قابل للاختبار، اكتب اختبارات وحدية لكل composable، وادمج أدوات قياس الأداء في CI. موارد إضافية تتضمن التوثيق الرسمي لـ Vue 3، مقالات عن composables، وكتب حول تصميم المكونات وعمارة الواجهات.
🧠 اختبر معرفتك
اختبر معرفتك
تحدى نفسك مع هذا الاختبار التفاعلي واكتشف مدى فهمك للموضوع
📝 التعليمات
- اقرأ كل سؤال بعناية
- اختر أفضل إجابة لكل سؤال
- يمكنك إعادة الاختبار عدة مرات كما تريد
- سيتم عرض تقدمك في الأعلى