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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر

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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر

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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر

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

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

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

فريق نوفيل١ أغسطس ٢٠٢٦12 دقائق قراءة١١ مشاهدة

عندما تفتح متصفح المستخدم وتكتب عنوان موقعك، لا أحد يرى الـ 500 مللي ثانية التي يستغرقها الـ Event Loop في معالجة الـ DOM Diffing، ولا الـ 3 ميجابايت من الـ JavaScript التي تُحمّل في الخلفية. لكن المستخدم يشعر بها فوراً: الزر لا يستجيب، الصفحة تتعطل، ثم يغلق التبويب. الحقيقة هي أن بناء تطبيق Vue 3 للإنتاج ليس مجرد كتابة مكونات جميلة، بل هو هندسة نظام متكامل يتحمل الضغط، يتوسع مع المستخدمين، ولا يترك ثغرات للأمان. في هذا الدليل، سأريك بالضبط كيف نبني تطبيقاً من الصفر لا يمر فقط باختبارات QA، بل ينجح في العالم الحقيقي حيث الـ I/O Bound Tasks تبطئ السيرفر، والـ Memory Leaks تلتهم الرام، والمستخدمون يضغطون على الأزرار بسرعة أكبر مما تتوقع.

سنتجاوز النظريات الأكاديمية ونذهب مباشرة إلى ما يهم: كيف تختار الـ Bundler المناسب؟ متى تستخدم Composition API ومتى تبقى مع Options API؟ كيف تضمن أن تطبيقك لن يتجمد عند تحميل 10 آلاف صف في الجدول؟ وكيف تنشره على الإنتاج دون أن تفقد السيطرة على الـ Rollback؟ كل خطوة مدعومة بكود حقيقي قابل للتشغيل، ومشاكل حقيقية واجهتها في مشاريع مثل منصة تعليمية تعالج 50 ألف طلب يومياً، وتطبيق إدارة مالية يتعامل مع بيانات حساسة. لنبدأ من الصفر، لكن لن نبقى فيه طويلاً.

تهيئة المشروع: لا تبدأ بـ create-vue قبل أن تعرف ماذا يحدث خلف الكواليس

الكثير يبدأ بـ npm create vue@latest وينتهي به الأمر بمشروع مليء بالملفات التي لا يفهمها، وdependencies لا يعرف لماذا أضيفت. لكن المهندس الجيد يعرف أن كل سطر في package.json يؤثر على الأداء النهائي. مثلاً، هل تعلم أن إضافة vue-router وpinia في البداية يضيف 20 كيلوبايت إلى حجم الـ Bundle حتى لو لم تستخدمهما؟ أو أن استخدام TypeScript يضيف 15% إلى وقت البناء بسبب الـ Type Checking؟ هذه ليست تفاصيل تافهة، بل هي الفارق بين تطبيق سريع وآخر بطيء.

لنبدأ بالأساسيات الصحيحة. أولاً، نختار الـ Bundler: Vite أم Webpack؟ في عام 2024، الإجابة واضحة: Vite. لماذا؟ لأن Vite يستخدم ES Modules مباشرة في التطوير، مما يعني أن تغيير ملف واحد يعيد تحميل المكون فقط دون إعادة بناء المشروع بالكامل. في مشروع متوسط الحجم، هذا يقلل وقت التطوير من 5 ثوانٍ إلى 50 مللي ثانية. لكن Webpack لا يزال له مكان في المشاريع الكبيرة التي تحتاج إلى تحسينات متقدمة مثل Code Splitting الديناميكي أو تحليل الـ Bundle. في هذا الدليل، سنستخدم Vite لأنه الخيار الأمثل للمشاريع الجديدة.

bash
# إنشاء مشروع Vue 3 باستخدام Vite
npm create vite@latest my-vue-app -- --template vue-ts
cd my-vue-app
npm install

# تثبيت المكتبات الأساسية للإنتاج
npm install vue-router pinia axios lodash
npm install --save-dev @types/lodash @vitejs/plugin-vue @vue/test-utils

# تثبيت أدوات الجودة
npm install --save-dev eslint prettier husky lint-staged
npx husky-init && npm install
npx lint-staged --install

لاحظ أننا أضفنا TypeScript منذ البداية. قد يبدو الأمر معقداً في البداية، لكنه يوفر ساعات من تصحيح الأخطاء لاحقاً. مثلاً، عندما تكتب props في مكون، سيخبرك TypeScript فوراً إذا كان النوع غير متوافق، بدلاً من اكتشاف الخطأ في وقت التشغيل. أيضاً، أضفنا husky وlint-staged لضمان أن الكود المرسل إلى المستودع لا يحتوي على أخطاء تنسيق أو مشاكل في الـ Linting. هذه الأدوات ليست رفاهية، بل هي ضرورية في أي مشروع إنتاجي.

تهيئة بيئة التطوير: لا تترك الـ Dev Server يعمل كما هو

الـ Dev Server الافتراضي لـ Vite جيد، لكنه ليس مثالياً للإنتاج. مثلاً، لا يدعم الـ HTTPS بشكل افتراضي، وهو أمر ضروري إذا كنت تختبر ميزات مثل الـ Service Workers أو الـ Geolocation API. أيضاً، لا يأتي مع إعدادات مخصصة للـ CORS، مما قد يسبب مشاكل عند الاتصال بـ APIs خارجية. إليك كيفية تهيئته بشكل صحيح:

javascript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'

// https://vitejs.dev/config/
export default defineConfig({
 plugins: [vue()],
 resolve: {
 alias: {
 '@': fileURLToPath(new URL('./src', import.meta.url))
 }
 },
 server: {
 https: true, // تفعيل HTTPS
 proxy: {
 '/api': {
 target: 'https://api.example.com',
 changeOrigin: true,
 rewrite: (path) => path.replace(/^\/api/, '')
 }
 },
 port: 3000,
 strictPort: true // يمنع تغيير المنفذ إذا كان مشغولاً
 },
 build: {
 outDir: 'dist',
 emptyOutDir: true,
 sourcemap: true // مهم لتصحيح الأخطاء في الإنتاج
 }
})

لاحظ أننا أضفنا alias لـ @ ليشير إلى مجلد src. هذا يجعل الاستيرادات أكثر نظافة وأقل عرضة للأخطاء. مثلاً، بدلاً من كتابة ../../components/Button، يمكنك كتابة @/components/Button. أيضاً، أضفنا sourcemap في إعدادات البناء، وهو أمر ضروري لتصحيح الأخطاء في الإنتاج. بدونها، سترى أخطاء تشير إلى سطور غير موجودة في الكود الأصلي، مما يجعل تصحيح الأخطاء شبه مستحيل.


بنية المشروع: لا تتبع التوجيهات العمياء، صممها حسب احتياجاتك

الكثير يتبع بنية المشروع التي يولدها create-vue بدون تفكير: components، views، router، store. لكن هذه البنية ليست مثالية لكل مشروع. مثلاً، في تطبيق معقد يحتوي على ات المكونات، قد تجد نفسك تبحث عن مكون محدد بين مجلدات متداخلة. أو قد تجد أن مجلد views يحتوي على مكونات كبيرة ومعقدة يصعب صيانتها. الحل؟ تصميم بنية تناسب حجم وتعقيد مشروعك.

في مشاريع الإنتاج، أفضل استخدام بنية تعتمد على الميزة (Feature-based) بدلاً من النوع (Type-based). مثلاً، بدلاً من فصل جميع المكونات في مجلد components، نقسم المشروع حسب الميزات مثل auth، dashboard، settings. كل ميزة تحتوي على مكوناتها، اختباراتها، وأنماطها الخاصة. هذا يجعل المشروع أكثر قابلية للتوسع ويسهل على الفرق الكبيرة العمل على ميزات مختلفة دون تعارضات. إليك مثال على بنية مشروع متوسط الحجم:

bash
src/
├── assets/
│ ├── fonts/
│ └── images/
├── common/
│ ├── components/ # مكونات مشتركة مثل الأزرار والنماذج
│ ├── composables/ # الـ Composable Functions المشتركة
│ ├── utils/ # دوال مساعدة
│ └── styles/ # أنماط عامة
├── features/
│ ├── auth/
│ │ ├── components/
│ │ ├── composables/
│ │ ├── stores/
│ │ ├── types/
│ │ └── index.ts
│ ├── dashboard/
│ │ ├── components/
│ │ ├── ...
│ └── settings/
├── router/
│ └── index.ts
├── stores/
│ └── index.ts
├── App.vue
└── main.ts

لاحظ أننا فصلنا المكونات المشتركة في مجلد common/components، بينما المكونات الخاصة بكل ميزة موجودة داخل مجلد الميزة. هذا يجعل من السهل حذف ميزة كاملة دون التأثير على باقي المشروع. أيضاً، أضفنا مجلد types لكل ميزة لتخزين الـ Interfaces وTypes الخاصة بها، مما يجعل الكود أكثر تنظيماً وأسهل في الصيانة.

إدارة الحالة: Pinia أم Composition API المحلي؟

في Vue 3، لديك خياران لإدارة الحالة: استخدام Pinia (الخليفة الرسمي لـ Vuex) أو إدارة الحالة محلياً باستخدام Composition API. الخيار ليس عشوائياً، بل يعتمد على حجم وتعقيد التطبيق. مثلاً، في تطبيق صغير يحتوي على بضعة مكونات، قد لا تحتاج إلى Pinia على الإطلاق. لكن في تطبيق يحتوي على عشرات المكونات التي تشارك نفس الحالة، فإن استخدام Pinia سيوفر عليك ساعات من تصحيح الأخطاء الناتجة عن تمرير الـ Props بشكل متكرر.

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

javascript
 setup lang="ts">
import { ref } from 'vue'

interface Task {
 id: number
 title: string
 completed: boolean
}

const tasks = ref<Task[]>([])

function addTask(title: string) {
 tasks.value.push({
 id: Date.now(),
 title,
 completed: false
 })
}

function toggleTask(id: number) {
 const task = tasks.value.find(task => task.id === id)
 if (task) task.completed = !task.completed
}

function deleteTask(id: number) {
 tasks.value = tasks.value.filter(task => task.id !== id)
}
</script>

<template>
 <div>
 <input v-model="newTask" @keyup.enter="addTask(newTask); newTask = ''" />
 <ul>
 <li v-for="task in tasks" :key="task.id">
 <input type="checkbox" v-model="task.completed" @change="toggleTask(task.id)" />
 {{ task.title }}
 <button @click="deleteTask(task.id)">حذف</button>
 </li>
 </ul>
 </div>
</template>

هذا الكود يعمل بشكل جيد لتطبيق صغير، لكن ماذا لو احتجت إلى مشاركة هذه الحالة بين عدة مكونات؟ ستضطر إلى تمرير الـ Props بشكل متكرر، أو استخدام provide/inject، مما يجعل الكود معقداً وصعب الصيانة. هنا يأتي دور Pinia. إليك نفس المثال باستخدام Pinia:

javascript
// stores/tasks.ts
import { defineStore } from 'pinia'

interface Task {
 id: number
 title: string
 completed: boolean
}

export const useTasksStore = defineStore('tasks', {
 state: () => ({
 tasks: [] as Task[]
 }),
 actions: {
 addTask(title: string) {
 this.tasks.push({
 id: Date.now(),
 title,
 completed: false
 })
 },
 toggleTask(id: number) {
 const task = this.tasks.find(task => task.id === id)
 if (task) task.completed = !task.completed
 },
 deleteTask(id: number) {
 this.tasks = this.tasks.filter(task => task.id !== id)
 }
 }
})

الآن يمكنك استخدام هذا المتجر في أي مكون بسهولة:

javascript
 setup lang="ts">
import { useTasksStore } from '@/stores/tasks'

const tasksStore = useTasksStore()
const newTask = ref('')
</script>

<template>
 <div>
 <input v-model="newTask" @keyup.enter="tasksStore.addTask(newTask); newTask = ''" />
 <ul>
 <li v-for="task in tasksStore.tasks" :key="task.id">
 <input type="checkbox" v-model="task.completed" @change="tasksStore.toggleTask(task.id)" />
 {{ task.title }}
 <button @click="tasksStore.deleteTask(task.id)">حذف</button>
 </li>
 </ul>
 </div>
</template>

الفرق واضح: الكود أصبح أكثر نظافة وأسهل في الصيانة. أيضاً، Pinia يوفر ميزات إضافية مثل الـ DevTools Integration، مما يجعل تصحيح الأخطاء أسهل بكثير. لكن تذكر أن Pinia يضيف حجماً إلى الـ Bundle، لذا استخدمه فقط عندما تحتاج إليه حقاً.


الأداء: لا تدع تطبيقك يتجمد عند تحميل 10 آلاف صف

أحد أكبر الأخطاء التي أراها في تطبيقات Vue هو تحميل البيانات بشكل كامل في الذاكرة دون تفكير. مثلاً، تطبيق يعرض قائمة عملاء تحتوي على 10 آلاف صف. إذا قمت بتحميل جميع البيانات دفعة واحدة، سيستهلك التطبيق مئات الميجابايتات من الرام، وسيتجمد المتصفح عند محاولة التمرير. الحل؟ التحميل الكسول (Lazy Loading) والتقسيم (Pagination).

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

javascript
 setup lang="ts">
import { ref, onMounted } from 'vue'
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

interface Customer {
 id: number
 name: string
 email: string
 phone: string
}

const customers = ref<Customer[]>([])
const loading = ref(false)
const page = ref(1)
const pageSize = ref(20)
const total = ref(0)

async function fetchCustomers() {
 loading.value = true
 try {
 const resp await fetch(`https://api.example.com/customers?page=${page.value}&pageSize=${pageSize.value}`)
 const data = await response.json()
 customers.value = [...customers.value, ...data.items]
 total.value = data.total
 } catch (error) {
 console.error('Failed to fetch customers:', error)
 } finally {
 loading.value = false
 }
}

function loadMore() {
 if (customers.value.length < total.value) {
 page.value++
 fetchCustomers()
 }
}

onMounted(fetchCustomers)
</script>

<template>
 <div class="customer-table">
 <RecycleScroller
 class="scroller"
 :items="customers"
 :item-size="50"
 key-field="id"
 v-slot="{ item }"
 @bottom="loadMore"
 >
 <div class="customer-row">
 <div>{{ item.name }}</div>
 <div>{{ item.email }}</div>
 <div>{{ item.phone }}</div>
 </div>
 </RecycleScroller>
 <div v-if="loading" class="loading">جارٍ التحميل...</div>
 </div>
</template>

<style scoped>
.customer-table {
 height: 500px;
 overflow-y: auto;
}

.customer-row {
 display: flex;
 padding: 10px;
 border-bottom: 1px solid #eee;
}

.loading {
 padding: 10px;
 text-align: center;
}
</style>

لاحظ أننا استخدمنا RecycleScroller لعرض الصفوف المرئية فقط، مما يقلل من عدد العناصر في الـ DOM بشكل كبير. أيضاً، استخدمنا التحميل الكسول عند الوصول إلى نهاية القائمة (loadMore). هذا يجعل التطبيق سريعاً حتى مع كميات كبيرة من البيانات. لكن تذكر أن التحميل الكسول له تحدياته الخاصة، مثل إدارة حالة التحميل وتجنب الطلبات المكررة.

تحسين أداء الـ Rendering: لا تدع الـ Virtual DOM يبطئك

Vue يستخدم الـ Virtual DOM لتحسين أداء الـ Rendering، لكنه ليس سحرياً. إذا كتبت كوداً غير فعال، سيصبح التطبيق بطيئاً حتى مع الـ Virtual DOM. مثلاً، استخدام v-for مع قوائم كبيرة بدون key، أو استخدام computed properties مع دوال ثقيلة، يمكن أن يسبب مشاكل أداء كبيرة.

إليك بعض النصائح لتحسين أداء الـ Rendering:

  • •استخدم key مع v-for: دائماً استخدم key فريد مع v-for لتجنب إعادة بناء الـ DOM بالكامل عند تغيير القائمة.
  • •تجنب الـ Deep Watching: إذا كنت تستخدم watch مع كائن معقد، استخدم { deep: false } إذا كان ذلك ممكناً.
  • •استخدم computed properties بحكمة: إذا كانت الـ computed property تعتمد على بيانات كبيرة، فكر في استخدام memoization أو تقسيمها إلى أجزاء أصغر.
  • •تجنب الـ Inline Functions في القوالب: كتابة دوال مباشرة في القوالب مثل @click="() => doSomething()" يسبب إعادة إنشاء الدالة في كل مرة يتم فيها الـ Rendering.
  • •استخدم v-once للعناصر الثابتة: إذا كان لديك عنصر لا يتغير أبداً، استخدم v-once لتجنب إعادة الـ Rendering.
  • •استخدم v-memo للتقليل من إعادة الـ Rendering: في Vue 3.2+، يمكنك استخدام v-memo لتجنب إعادة الـ Rendering للعناصر التي لم تتغير قيمها.

لنأخذ مثالاً على استخدام v-memo:

javascript
<template>
 <div>
 <div v-for="item in items" :key="item.id" v-memo="[item.id, item.name]">
 {{ item.name }} - {{ item.value }}
 </div>
 </div>
</template>

 setup lang="ts">
import { ref } from 'vue'

const items = ref([
 { id: 1, name: 'Item 1', value: 100 },
 { id: 2, name: 'Item 2', value: 200 },
 { id: 3, name: 'Item 3', value: 300 }
])

// تغيير قيمة عنصر دون تغيير الاسم
function updateValue() {
 items.value[0].value = 400
}
</script>

في هذا المثال، استخدمنا v-memo مع مصفوفة تحتوي على item.id وitem.name. هذا يعني أن العنصر لن يعاد الـ Rendering إلا إذا تغيرت قيمة id أو name. حتى إذا تغيرت قيمة value، فلن يعاد الـ Rendering، مما يحسن الأداء بشكل كبير في القوائم الكبيرة.


النشر على الإنتاج: لا تدع الـ CI/CD يكون مجرد زر ضغط

الكثير يعتقد أن النشر على الإنتاج هو مجرد تشغيل npm run build ثم رفع الملفات إلى السيرفر. لكن الحقيقة أكثر تعقيداً. مثلاً، كيف تضمن أن النسخة الجديدة لا تحتوي على أخطاء؟ كيف تتعامل مع الـ Rollback إذا حدث خطأ؟ كيف تضمن أن المستخدمين لا يرون صفحة بيضاء أثناء التحديث؟ هذه الأسئلة ليست نظرية، بل هي مشاكل حقيقية تواجهها في الإنتاج.

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

  • •تحليل الـ Bundle: استخدم أدوات مثل rollup-plugin-visualizer لتحليل حجم الملفات وتحديد المكتبات الكبيرة.
  • •تقسيم الكود: استخدم dynamic imports لتقسيم الكود إلى أجزاء أصغر تُحمّل عند الحاجة فقط.
  • •تهيئة الـ Environment Variables: استخدم ملفات .env مختلفة للتطوير والإنتاج.
  • •تفعيل الـ Compression: استخدم compression-webpack-plugin لتقليل حجم الملفات.
  • •تهيئة الـ Cache: استخدم استراتيجيات التخزين المؤقت المناسبة لضمان تحميل الملفات بسرعة.

إليك مثال على كيفية إعداد ملفات البيئة:

bash
# .env.development
VITE_API_URL=http://localhost:3000/api
VITE_APP_MODE=development

# .env.production
VITE_API_URL=https://api.example.com
VITE_APP_MODE=production

ثم في الكود، يمكنك الوصول إلى هذه المتغيرات باستخدام import.meta.env:

javascript
const apiUrl = import.meta.env.VITE_API_URL
const appMode = import.meta.env.VITE_APP_MODE

إعداد الـ CI/CD: لا تدع النشر يعتمد على ذاكرة المطور

النشر اليدوي خطير ومضيعة للوقت. بدلاً من ذلك، استخدم نظام CI/CD مثل GitHub Actions أو GitLab CI لنشر التطبيق تلقائياً عند كل تغيير في المستودع. إليك مثال على ملف GitHub Actions لنشر تطبيق Vue على Vercel:

yaml
name: Deploy to Vercel

on:
 push:
 branches: [ main ]

jobs:
 deploy:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v3

 - name: Install Node.js
 uses: actions/setup-node@v3
 with:
 node-version: 18

 - name: Install dependencies
 run: npm install

 - name: Run tests
 run: npm test

 - name: Build project
 run: npm run build

 - name: Deploy to Vercel
 run: npx vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
 env:
 VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
 VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

هذا الملف يفعل عدة أشياء مهمة: أولاً، يتحقق من الكود من المستودع. ثم، يقوم بتثبيت Node.js والـ Dependencies. بعد ذلك، يقوم بتشغيل الاختبارات لضمان أن الكود لا يحتوي على أخطاء. أخيراً، يقوم ببناء المشروع ونشره على Vercel. لاحظ أننا نستخدم secrets لتخزين الـ Tokens الحساسة، مما يمنع تسربها في سجلات الـ CI.

استراتيجيات النشر: لا تدع المستخدمين يرون صفحة بيضاء

عند نشر نسخة جديدة من التطبيق، هناك احتمال أن المستخدمين يرون صفحة بيضاء إذا حدث خطأ أثناء التحميل. الحل؟ استخدام استراتيجيات النشر الذكية مثل الـ Blue-Green Deployment أو الـ Canary Releases. لكن أبسط حل هو استخدام ميزة الـ Service Worker لتخزين النسخة السابقة من التطبيق مؤقتاً.

إليك كيفية إعداد Service Worker باستخدام Workbox:

javascript
// vite.config.ts
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
 plugins: [
 VitePWA({
 registerType: 'autoUpdate',
 includeAssets: ['favicon.ico', 'apple-touch-icon.png'],
 manifest: {
 name: 'My Vue App',
 short_name: 'VueApp',
 description: 'My Awesome Vue App',
 theme_color: '#ffffff',
 icons: [
 {
 src: 'pwa-192x192.png',
 sizes: '192x192',
 type: 'image/png'
 },
 {
 src: 'pwa-512x512.png',
 sizes: '512x512',
 type: 'image/png'
 }
 ]
 },
 workbox: {
 runtimeCaching: [
 {
 urlPattern: /^https:\/\/api\.example\.com\/.*/i,
 handler: 'NetworkFirst',
 options: {
 cacheName: 'api-cache',
 expiration: {
 maxEntries: 10,
 maxAgeSeconds: 60 * 60 * 24 // 24 ساعة
 }
 }
 },
 {
 urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/i,
 handler: 'CacheFirst',
 options: {
 cacheName: 'image-cache',
 expiration: {
 maxEntries: 50,
 maxAgeSeconds: 60 * 60 * 24 * 7 // أسبوع
 }
 }
 }
 ]
 }
 })
 ]
})

هذا الإعداد يفعل عدة أشياء: أولاً، يسجل Service Worker تلقائياً عند تحميل الصفحة. ثم، يحدد استراتيجيات التخزين المؤقت للملفات الثابتة وAPIs. مثلاً، يستخدم NetworkFirst للتخزين المؤقت لـ APIs، مما يعني أنه سيحاول أولاً الحصول على البيانات من الشبكة، وإذا فشل، سيستخدم النسخة المخزنة مؤقتاً. أما بالنسبة للصور، يستخدم CacheFirst للحصول على الصور من التخزين المؤقت أولاً، مما يحسن أداء التحميل.


الأمان: لا تدع تطبيقك يصبح بوابة للمخترقين

الأمان ليس شيئاً تضيفه في النهاية، بل هو جزء من عملية التطوير منذ البداية. مثلاً، إذا كنت تستخدم Vue مع API خارجي، كيف تضمن أن البيانات المرسلة والمستقبلة آمنة؟ كيف تمنع هجمات XSS وCSRF؟ كيف تخزن الـ Tokens الحساسة بشكل آمن؟ هذه ليست أسئلة نظرية، بل هي مشاكل حقيقية تواجهها في الإنتاج.

لنبدأ بأهم قاعدة: لا تثق أبداً بالبيانات القادمة من المستخدم. حتى إذا كنت تستخدم Vue، الذي يحمي من XSS بشكل افتراضي، يجب أن تكون حذراً عند عرض البيانات الديناميكية. مثلاً، إذا كنت تعرض محتوى من API خارجي، يجب تطهيره قبل عرضه. إليك كيفية استخدام مكتبة DOMPurify لتطهير المحتوى:

javascript
 setup lang="ts">
import { ref, onMounted } from 'vue'
import DOMPurify from 'dompurify'

const userC ref('')

onMounted(async () => {
 const response = await fetch('https://api.example.com/user-content')
 const data = await response.json()
 userContent.value = DOMPurify.sanitize(data.content)
})
</script>

<template>
 <div v-html="userContent"></div>
</template>

لاحظ أننا استخدمنا v-html لعرض المحتوى، لكننا قمنا بتطهيره أولاً باستخدام DOMPurify. هذا يمنع هجمات XSS عن طريق إزالة أي أكواد JavaScript ضارة من المحتوى.

حماية الـ API Calls: لا تدع الـ Tokens الحساسة تسرب

عند الاتصال بـ API خارجي، يجب أن تكون حذراً جداً مع الـ Tokens الحساسة. مثلاً، تخزين الـ Token في localStorage غير آمن لأنه يمكن الوصول إليه عبر JavaScript. بدلاً من ذلك، استخدم HttpOnly Cookies لتخزين الـ Tokens، مما يمنع الوصول إليها عبر JavaScript. إليك كيفية إعداد axios لاستخدام HttpOnly Cookies:

javascript
// axios.ts
import axios from 'axios'

const api = axios.create({
 baseURL: import.meta.env.VITE_API_URL,
 withCredentials: true // يسمح بإرسال واستقبال الكوكيز
})

// إضافة Interceptor لإضافة الـ Token إلى كل طلب
api.interceptors.request.use((config) => {
 // إذا كنت تستخدم JWT في localStorage (غير موصى به)
 // const token = localStorage.getItem('token')
 // if (token) {
 // config.headers.Authorization = `Bearer ${token}`
 // }
 return config
})

// إضافة Interceptor لمعالجة الأخطاء
api.interceptors.response.use(
 (response) => response,
 (error) => {
 if (error.response?.status === 401) {
 // إعادة التوجيه إلى صفحة تسجيل الدخول
 window.location.href = '/login'
 }
 return Promise.reject(error)
 }
)

export default api

لاحظ أننا استخدمنا withCredentials: true للسماح بإرسال واستقبال الكوكيز. هذا يعني أن السيرفر يجب أن يرسل الـ Token في كوكي HttpOnly، وليس في الـ Response Body. أيضاً، أضفنا Interceptor لمعالجة الأخطاء، مثل إعادة التوجيه إلى صفحة تسجيل الدخول إذا تلقينا خطأ 401.

حماية من CSRF: لا تدع المهاجمين يرسلون طلبات نيابة عنك

هجمات CSRF تحدث عندما يرسل المهاجم طلبات نيابة عن المستخدم دون علمه. لمنع هذه الهجمات، يجب استخدام CSRF Tokens. إليك كيفية إعدادها في تطبيق Vue:

javascript
// في مكون تسجيل الدخول
 setup lang="ts">
import { ref } from 'vue'
import api from '@/axios'

const email = ref('')
const password = ref('')
const csrfToken = ref('')

async function login() {
 try {
 // أولاً، الحصول على CSRF Token
 const csrfResp await api.get('/csrf-token')
 csrfToken.value = csrfResponse.data.csrfToken

 // ثم، إرسال طلب تسجيل الدخول مع CSRF Token
 await api.post('/login', {
 email: email.value,
 password: password.value
 }, {
 headers: {
 'X-CSRF-Token': csrfToken.value
 }
 })
 } catch (error) {
 console.error('Login failed:', error)
 }
}
</script>

في هذا المثال، نقوم أولاً بالحصول على CSRF Token من السيرفر، ثم نرسل طلب تسجيل الدخول مع هذا Token في الـ Headers. السيرفر سيتحقق من هذا Token قبل معالجة الطلب، مما يمنع هجمات CSRF. لاحظ أن هذا يتطلب إعداد السيرفر لإصدار والتحقق من CSRF Tokens، وهو ما يجب القيام به في الـ Backend.


خلاصة المهندس: لا تترك الإنتاج للصدفة

بناء تطبيق Vue 3 للإنتاج ليس مجرد كتابة مكونات جميلة، بل هو هندسة نظام متكامل يتحمل الضغط، يتوسع مع المستخدمين، ويبقى آمناً. من تجربتي في بناء تطبيقات تعالج آلاف الطلبات يومياً، تعلمت أن التفاصيل الصغيرة تصنع الفارق الكبير. مثلاً، استخدام v-memo في قائمة تحتوي على 10 آلاف عنصر يمكن أن يقلل وقت الـ Rendering من 500 مللي ثانية إلى 50 مللي ثانية. أو استخدام Service Worker يمكن أن يمنع المستخدمين من رؤية صفحة بيضاء أثناء التحديثات.

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

إذا كنت تريد خطوة تالية، ابدأ بتحليل أداء تطبيقك الحالي باستخدام Lighthouse، وحدد نقطة ضعف واحدة (مثل حجم الـ Bundle أو وقت التحميل) واعمل على تحسينها. ستندهش من الفرق الذي يمكن أن تصنعه التفاصيل الصغيرة.

Vue3 الإنتاج الأداء الأمان CI/CD Pinia

التعليقات

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

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

المنصة

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

الحساب

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

روابط

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

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

صُنع بـ في مصر