نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/Vue
Vue

Vue 3 من الصفر للإنتاج: دليل المهندس لبناء تطبيقات سريعة وآمنة دون تعقيدات

إرسال ٣ ميجابايت من JavaScript لمتصفح المستخدم ليس مجرد بطء، بل جريمة ضد تجربة المستخدم. في هذا الدليل العملي، سنبني تطبيق Vue 3 متكاملاً من الصفر للإنتاج، مع التركيز على الأداء والأمان والبنية القابلة للتوسع دون الوقوع في فخاخ الـ Over-Engineering.

فريق نوفيل١٥ يوليو ٢٠٢٦7 دقائق قراءة٧ مشاهدة

في المثال السابق، استخدام `computed` بدلاً من `v-if` مع `v-for` يقلل من عدد عمليات إعادة الرسم بشكل كبير. لماذا؟ لأن `computed` يحفظ النتيجة ويعيد حسابها فقط عندما تتغير البيانات المعتمدة عليها، بينما `v-if` مع `v-for` يعيد تقييم الشرط في كل دورة من الـ Event Loop. هذا الفرق قد يبدو بسيطاً، لكنه يصبح كارثياً عندما يكون لديك قائمة تحتوي على مئات العناصر.

الـ Lazy Loading للمكونات: لماذا وكيف؟

تحميل جميع مكونات التطبيق عند بدء التشغيل هو خطأ شائع آخر. بدلاً من ذلك، يجب تحميل المكونات فقط عندما يحتاجها المستخدم. في Vue Router، يمكنك القيام بذلك بسهولة باستخدام `() => import()`. مثلاً:

javascript
const routes = [
 {
 path: '/dashboard',
 name: 'Dashboard',
 component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue')
 },
 {
 path: '/settings',
 name: 'Settings',
 component: () => import(/* webpackChunkName: "settings" */ '@/views/Settings.vue')
 }
];

في هذا المثال، المكونات `Dashboard.vue` و `Settings.vue` لن تُحمّل إلا عندما يزور المستخدم المسار `/dashboard` أو `/settings`. هذا يقلل من حجم الحزمة الأولية بشكل كبير. لكن هناك مشكلة شائعة هنا: إذا كان لديك مكونات مشتركة بين عدة مسارات، مثل مكون `Header` أو `Sidebar`، فقد ينتهي بك الأمر بتكرار الكود في عدة ملفات. الحل هو استخدام `webpackChunkName` نفسه للمكونات المشتركة، بحيث يتم تحميلها مرة واحدة فقط.

الـ Virtual Scrolling للقوائم الطويلة

إذا كان تطبيقك يحتوي على قوائم طويلة، مثل قائمة المنتجات أو الرسائل، فإن استخدام `v-for` بشكل مباشر سيؤدي إلى بطء شديد في المتصفح. الحل هو استخدام تقنية الـ Virtual Scrolling، التي تقوم بعرض العناصر المرئية فقط على الشاشة وتحميل الباقي عند التمرير. هناك مكتبات جاهزة مثل `vue-virtual-scroller`، لكن يمكنك أيضاً بناء حل بسيط بنفسك:

html
<template>
 <div class="virtual-list" @scroll="handleScroll">
 <div class="virtual-list-container" :style="{ height: totalHeight + 'px' }">
 <div
 v-for="item in visibleItems"
 :key="item.id"
 class="virtual-list-item"
 :style="{ transform: `translateY(${item.top}px)` }"
 >
 {{ item.name }}
 </div>
 </div>
 </div>
</template>

 setup>
import { ref, computed, onMounted } from 'vue';

const items = ref(Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `Item ${i}` })));
const itemHeight = 50;
const visibleCount = ref(10);
const scrollTop = ref(0);

const totalHeight = computed(() => items.value.length * itemHeight);
const startIndex = computed(() => Math.floor(scrollTop.value / itemHeight));
const visibleItems = computed(() => {
 const endIndex = Math.min(startIndex.value + visibleCount.value, items.value.length);
 return items.value.slice(startIndex.value, endIndex).map((item, index) => ({
 ...item,
 top: (startIndex.value + index) * itemHeight
 }));
});

function handleScroll(e) {
 scrollTop.value = e.target.scrollTop;
}
</script>

<style>
.virtual-list {
 height: 500px;
 overflow-y: auto;
 border: 1px solid #ccc;
}

.virtual-list-container {
 position: relative;
}

.virtual-list-item {
 position: absolute;
 width: 100%;
 height: 50px;
 box-sizing: border-box;
 border-bottom: 1px solid #eee;
}
</style>

في هذا المثال، بدلاً من عرض ١٠٠٠ عنصر في نفس الوقت، نقوم بعرض ١٠ عناصر فقط (يمكن تعديل العدد حسب الحاجة). عند التمرير، نقوم بتحديث `scrollTop` وإعادة حساب العناصر المرئية. هذا يقلل من عدد العناصر في الـ DOM بشكل كبير، مما يحسن الأداء بشكل ملحوظ. لاحظ أن هذا الحل بسيط ولا يدعم جميع الحالات، مثل القوائم ذات الارتفاعات المتغيرة، لكنه جيد كنقطة بداية.


إدارة الحالة باستخدام Pinia: تجنب الفخاخ الشائعة

Vuex كان الخيار الأساسي لإدارة الحالة في Vue 2، لكن مع Vue 3، أصبح Pinia هو الخيار الموصى به. الفرق الرئيسي بين Vuex وPinia هو أن Pinia أبسط وأكثر مرونة، لكنه يتطلب أيضاً فهماً أعمق لكيفية عمل الـ Reactivity في Vue. أحد الأخطاء الشائعة التي أراها هو تخزين بيانات غير تفاعلية في الـ Store، مثل الكائنات الكبيرة أو البيانات التي لا تتغير. مثلاً:

javascript
// خطأ: تخزين بيانات غير تفاعلية في Store
import { defineStore } from 'pinia';

export const useC defineStore('config', {
 state: () => ({
 config: {
 apiUrl: 'https://api.example.com',
 maxItems: 100,
 theme: 'light'
 }
 })
});

في هذا المثال، الكائن `config` لا يتغير أبداً، لذلك ليس هناك حاجة لتخزينه في الـ Store. بدلاً من ذلك، يمكنك تخزينه في ملف ثابت واستيراده عند الحاجة. هذا يقلل من حجم الـ Store ويحسن الأداء. قاعدة عامة: إذا كانت البيانات لا تتغير أو لا تحتاج إلى تفاعل، فلا تضعها في الـ Store.

الـ Actions في Pinia: متى تستخدمها ومتى تتجنبها

في Pinia، الـ Actions هي المكان المناسب لوضع المنطق المعقد، مثل جلب البيانات من الـ API أو معالجة البيانات. لكن الكثير من المطورين يضعون كل شيء في الـ Actions، حتى العمليات البسيطة التي يمكن تنفيذها مباشرة في المكونات. هذا خطأ لأن الـ Actions تُنشئ طبقة إضافية من التعقيد دون داعٍ. مثلاً:

javascript
// خطأ: وضع منطق بسيط في Action
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
 state: () => ({
 count: 0
 }),
 actions: {
 increment() {
 this.count++;
 }
 }
});

// في المكون:
const store = useCounterStore();
store.increment(); // غير ضروري

// الصحيح: تعديل الـ State مباشرة
const store = useCounterStore();
store.count++; // أفضل

في هذا المثال، الـ Action `increment` غير ضروري لأن العملية بسيطة ويمكن تنفيذها مباشرة. استخدم الـ Actions فقط عندما تحتاج إلى تنفيذ منطق معقد أو عمليات غير متزامنة. هذا يبقي الكود نظيفاً ويقلل من التعقيد.

الـ Getters في Pinia: تحسين الأداء باستخدام الـ Memoization

الـ Getters في Pinia مشابهة للـ Computed Properties في المكونات، لكنها تُستخدم لاستخراج البيانات من الـ Store. المشكلة هي أن الـ Getters تُعيد حسابها في كل مرة يتم الوصول إليها، حتى لو لم تتغير البيانات المعتمدة عليها. لحل هذه المشكلة، يمكنك استخدام مكتبة مثل `lodash.memoize` أو بناء حل بسيط بنفسك:

javascript
import { defineStore } from 'pinia';

export const useProductStore = defineStore('product', {
 state: () => ({
 products: []
 }),
 getters: {
 expensiveProducts(state) {
 // إعادة الحساب في كل مرة يتم الوصول إلى الـ Getter
 return state.products.filter(product => product.price > 100);
 },
 // Getter مع Memoization
 cheapProducts: (state) => {
 let cachedResult = null;
 return () => {
 if (!cachedResult) {
 cachedResult = state.products.filter(product => product.price <= 100);
 }
 return cachedResult;
 };
 }
 }
});

في هذا المثال، الـ Getter `cheapProducts` يحفظ النتيجة في `cachedResult` ويعيدها في المرات التالية دون إعادة الحساب. هذا مفيد جداً إذا كان لديك عمليات حسابية معقدة أو بيانات كبيرة. لكن لاحظ أن هذا الحل بسيط ولا يدعم التحديث التلقائي عند تغير البيانات، لذلك استخدمه بحذر.


تحسين الأداء للإنتاج: ما لا يخبرك به الوثائق

الوثائق الرسمية لـ Vue 3 تقدم نصائح جيدة لتحسين الأداء، لكنها لا تغطي كل شيء. مثلاً، هل تعلم أن استخدام `v-once` على المكونات التي لا تتغير يمكن أن يحسن الأداء بشكل كبير؟ أو أن تعطيل الـ Source Maps في الإنتاج يقلل من حجم الحزمة؟ دعنا نستعرض بعض النصائح التي لا تجدها في الوثائق:

  • •**استخدم `v-once` للمكونات الثابتة**: إذا كان لديك مكون لا يتغير أبداً، مثل `Header` أو `Footer`، يمكنك استخدام `v-once` لتجنب إعادة رسمه في كل تحديث. مثلاً: `<Header v-once />`. هذا يخبر Vue بعدم مراقبة هذا المكون للتغيرات، مما يحسن الأداء.
  • •**تعطيل الـ Source Maps في الإنتاج**: الـ Source Maps مفيدة في التطوير، لكنها تزيد من حجم الحزمة بشكل كبير. في ملف `vite.config.js`، يمكنك تعطيلها في وضع الإنتاج باستخدام:
  • •**استخدم `will-change` لتحسين الرسوم المتحركة**: إذا كان لديك عناصر متحركة، مثل القوائم أو الأزرار، يمكنك استخدام خاصية CSS `will-change` لتحسين أداء الرسوم المتحركة. مثلاً: `.animated-element { will-change: transform; }`. هذا يخبر المتصفح بأن هذه الخاصية ستتغير، مما يسمح له بتحسين الأداء.
  • •**تجنب الـ Deep Watchers**: استخدام `watch` مع الخيار `deep: true` يؤدي إلى مراقبة جميع الخصائص المتداخلة في الكائن، مما قد يسبب بطءاً شديداً. بدلاً من ذلك، استخدم `watch` مع مسار محدد أو استخدم `watchEffect` بحذر.
  • •**استخدم `shallowRef` بدلاً من `ref` للكائنات الكبيرة**: إذا كان لديك كائن كبير لا تحتاج إلى مراقبة جميع خصائصه، يمكنك استخدام `shallowRef` بدلاً من `ref`. هذا يقلل من عدد الخصائص التي يراقبها Vue، مما يحسن الأداء.
  • •**تحميل الخطوط والصور بشكل كسول**: استخدم سمة `loading="lazy"` للصور والخطوط لتحميلها فقط عندما تكون مرئية على الشاشة. مثلاً: `<img src="image.jpg" loading="lazy" alt="...">`.
javascript
export default defineConfig({
 build: {
 sourcemap: false // تعطيل Source Maps في الإنتاج
 }
});

ضغط وتحسين الكود باستخدام Webpack وVite

إذا كنت تستخدم Vue CLI، فأنت تستخدم Webpack خلف الكواليس. وإذا كنت تستخدم Vite، فأنت تستخدم Rollup. في كلتا الحالتين، يمكنك تحسين أداء البناء باستخدام بعض الإضافات والتكوينات. مثلاً، في Webpack، يمكنك استخدام `TerserPlugin` لضغط الكود و`CompressionPlugin` لضغط الملفات باستخدام Gzip أو Brotli. في Vite، يمكنك استخدام إضافات مثل `vite-plugin-compression` لتحقيق نفس الهدف.

javascript
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import compression from 'vite-plugin-compression';

export default defineConfig({
 plugins: [
 vue(),
 compression({
 algorithm: 'brotliCompress', // استخدام Brotli بدلاً من Gzip
 ext: '.br'
 })
 ]
});

في هذا المثال، نستخدم إضافة `vite-plugin-compression` لضغط الملفات باستخدام Brotli، الذي يوفر ضغطاً أفضل من Gzip. هذا يقلل من حجم الملفات بشكل كبير، مما يحسن وقت التحميل. لكن لاحظ أن Brotli يتطلب دعماً من السيرفر، لذلك تأكد من تفعيله على السيرفر الخاص بك.


النشر للإنتاج: الخطوات النهائية قبل الإطلاق

قبل نشر التطبيق للإنتاج، هناك بعض الخطوات النهائية التي يجب اتخاذها لضمان أن كل شيء يعمل بشكل مثالي. أولاً، تأكد من تشغيل التطبيق في وضع الإنتاج محلياً باستخدام:

bash
npm run build
npm run preview

هذا سيبني التطبيق في وضع الإنتاج ويعرضه محلياً باستخدام سيرفر خفيف. تحقق من أن كل شيء يعمل كما هو متوقع، خاصةً المكونات التي تعتمد على الـ Lazy Loading أو الـ Virtual Scrolling. بعد ذلك، تحقق من حجم الحزمة باستخدام أدوات مثل `webpack-bundle-analyzer` أو `rollup-plugin-visualizer`. هذه الأدوات تظهر لك حجم كل ملف ومكتبة، وتساعدك على تحديد الملفات الكبيرة التي يمكن تحسينها.

bash
npm install -D rollup-plugin-visualizer
# ثم أضف الإضافة إلى vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
 plugins: [
 vue(),
 visualizer({
 open: true // فتح التقرير تلقائياً بعد البناء
 })
 ]
});

أخيراً، تأكد من تفعيل التخزين المؤقت (Caching) على السيرفر الخاص بك. استخدم رؤوس HTTP مثل `Cache-Control` و`ETag` لضمان أن المتصفح يخزن الملفات الثابتة ولا يعيد تحميلها في كل زيارة. مثلاً، يمكنك إضافة هذه التكوينات إلى ملف `.htaccess` إذا كنت تستخدم Apache:

text
<FilesMatch "\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$">
 Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>

هذا يخبر المتصفح بتخزين الملفات الثابتة لمدة سنة كاملة وعدم إعادة طلبها من السيرفر إلا إذا تغيرت. هذا يقلل من عدد الطلبات إلى السيرفر ويحسن وقت التحميل بشكل كبير.


خلاصة المهندس: نصيحة واحدة تغني عن ألف سطر كود

بعد أكثر من عشر سنوات في بناء تطبيقات الويب، تعلمت أن الأداء ليس مجرد ميزة إضافية، بل هو أساس تجربة المستخدم. التطبيق الذي يعمل ببطء هو تطبيق فاشل، مهما كانت ميزاته رائعة. في Vue 3، المفتاح لبناء تطبيق سريع وآمن للإنتاج هو التركيز على التفاصيل الصغيرة التي لا يلاحظها معظم المطورين. مثلاً، استخدام `v-once` للمكونات الثابتة، أو تجزئة الكود باستخدام `() => import()`، أو تحسين الـ Getters في Pinia باستخدام الـ Memoization. هذه التفاصيل هي ما يميز التطبيق الجيد عن التطبيق السيئ.

نصيحة واحدة أخيرة: لا تحاول تحسين الأداء قبل أن تبني التطبيق. ابدأ ببنية بسيطة، ثم قم بقياس الأداء باستخدام أدوات مثل Lighthouse أو WebPageTest. بعد ذلك، قم بتحسين الأجزاء التي تسبب البطء فقط. هذا النهج يوفر الوقت والجهد ويضمن أن تحسن الأداء حيث يحتاجه التطبيق بالفعل. الآن، اذهب وبنِ شيئاً رائعاً!

Vue 3 إنتاج أداء Pinia Vue Router

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر