404 Not Found

404 Not Found


nginx

نظام الإضافات

يحتاج Charlie إلى دمج Stripe Payments SDK في MegaShop ويريد أيضاً خدمة تسجيل عامة. يُحمَّل Stripe فقط من جهة العميل، بينما تُسجَّل السجلات من جهة الخادم. تقليدياً، كان هذا يتطلب استيراد وتهيئة SDK يدوياً في أماكن متعددة، لكن نظام إضافات Nuxt 3 يؤتمت العملية بالكامل—سجّلها مرة، وتكون متاحة عالمياً.

1. ما ستتعلمه


2. قصة حقيقية لمهندس معمارية

(1) نقطة الألم: تهيئة مكتبات الطرف الثالث مبعثرة في الكود

يحتاج MegaShop الخاص بـ Charlie إلى دمج مدفوعات Stripe. حمّلت Alice ملف Stripe.js في صفحة سلة التسوق، وحمّله Bob مرة أخرى في صفحة الدفع—مما أدى إلى تحميلات مكررة—وفي بعض الصفحات نُسي، مما جعل زر الدفع غير متاح. نفس الشيء ينطبق على أداة التسجيل؛ كل مكوّن يتطلب استيراد المسجّل يدوياً.

(2) حل نظام إضافات Nuxt

إضافات Nuxt 3 تعمل تلقائياً وتُحقن عبر provide للاستخدام العام:

TYPESCRIPT
// plugins/stripe.client.ts
export default defineNuxtPlugin(() => {
  const stripe = Stripe(config.public.stripePublishableKey)
  return { provide: { stripe } }
})

(3) الفوائد: سجّل مرة للوصول العام

يتم تهيئة Stripe و Logger مرة واحدة فقط داخل الإضافة؛ جميع المكونات تصل عبر useNuxtApp().$stripe، مما يلغي الحاجة للتحميل المكرر أو الاستيراد اليدوي.


3. التسجيل التلقائي للإضافات

(1) توقيت تسجيل وتنفيذ الإضافات

100%
sequenceDiagram
    participant N as Nuxt App
    participant P1 as 01-env.server.ts
    participant P2 as 02-stripe.client.ts
    participant P3 as 03-logger.ts
    participant C as Components

    N->>P1: تنفيذ إضافات الخادم أولاً
    N->>P2: تنفيذ إضافات العميل عند الإرواء
    N->>P3: تنفيذ الإضافات العامة
    N->>C: يمكن للمكونات استخدام $stripe و $logger

(2) أنواع الإضافات واصطلاحات التسمية

الاسم بيئة التشغيل الغرض
plugins/xxx.ts SSR + العميل إضافة عامة
plugins/xxx.server.ts الخادم فقط قاعدة بيانات/مفاتيح
plugins/xxx.client.ts العميل فقط متصفح SDK

(1) ▶ مثال: إضافة تسجيل عامة

TYPESCRIPT
// plugins/logger.ts
export default defineNuxtPlugin(() => {
  const logger = {
    info: (message: string, data?: any) => {
      console.log(`[INFO] ${message}`, data || '')
    },
    warn: (message: string, data?: any) => {
      console.warn(`[WARN] ${message}`, data || '')
    },
    error: (message: string, data?: any) => {
      console.error(`[ERROR] ${message}`, data || '')
    }
  }

  return {
    provide: {
      logger
    }
  }
})

الناتج:

TEXT
// تم التنفيذ بنجاح

4. شرح تفصيلي لـ defineNuxtPlugin

(1) آلية Provide/Inject

الدور API الوصف
مقدم من الإضافة provide: { xxx } حقن $xxx في NuxtApp
استخدام المكوّن useNuxtApp().$xxx الوصول للخدمة المحقونة
استخدام Composable useNuxtApp().$xxx الوصول إلى Composable

(1) ▶ مثال: إضافة دفع Stripe (من جهة العميل)

TYPESCRIPT
// plugins/stripe.client.ts
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  const stripe = window.Stripe(config.public.stripePublishableKey)

  return {
    provide: {
      stripe
    }
  }
})

الناتج:

TEXT
// تم التنفيذ بنجاح

(2) ▶ مثال: استخدام إضافة في مكوّن

VUE
<!-- pages/checkout.vue -->
<template>
  <div>
    <h1>Checkout - ${{ total }} USD</h1>
    <div ref="cardElement"></div>
    <button @click="processPayment" :disabled="processing">
      Pay ${{ total }} USD
    </button>
  </div>
</template>

<script setup lang="ts">
const { $stripe } = useNuxtApp()
const { $logger } = useNuxtApp()

const cart = useState<any[]>('cart')
const total = computed(() => cart.value.reduce((s, i) => s + i.price * i.quantity, 0))
const processing = ref(false)

async function processPayment() {
  processing.value = true
  try {
    $logger.info('Processing payment', { amount: total.value })
    // استخدام Stripe للدفع
    const result = await $stripe.confirmCardPayment('{PAYMENT_INTENT_SECRET}')
    if (result.error) {
      $logger.error('Payment failed', result.error)
    } else {
      $logger.info('Payment succeeded')
      navigateTo('/order/success')
    }
  } finally {
    processing.value = false
  }
}
</script>

الناتج:

TEXT
// تم التنفيذ بنجاح

(3) ▶ مثال: إضافة قاعدة بيانات من جهة الخادم

TYPESCRIPT
// plugins/database.server.ts
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()

  // يعمل فقط على الخادم - اتصال قاعدة البيانات بالمفتاح الخاص
  const db = createDatabaseConnection(config.databaseUrl)

  return {
    provide: {
      db
    }
  }
})

الناتج:

TEXT
// تم التنفيذ بنجاح

5. التمييز بين إضافات الخادم والعميل

(1) ▶ مثال: إضافة الإشعارات (من جهة العميل، باستخدام واجهات برمجة المتصفح)

TYPESCRIPT
// plugins/notification.client.ts
export default defineNuxtPlugin(() => {
  function requestPermission() {
    if ('Notification' in window) {
      Notification.requestPermission()
    }
  }

  function send(title: string, body: string) {
    if (Notification.permission === 'granted') {
      new window.Notification(title, { body, icon: '/favicon.ico' })
    }
  }

  // طلب الإذن تلقائياً عند الزيارة الأولى
  requestPermission()

  return { provide: { notification: { send, requestPermission } } }
})

الناتج:

TEXT
// تم التنفيذ بنجاح

(2) ▶ مثال: إضافة كشف البيئة (عامة)

TYPESCRIPT
// plugins/env.ts
export default defineNuxtPlugin(() => {
  const isServer = import.meta.server
  const isClient = import.meta.client
  const isDev = import.meta.dev

  return {
    provide: {
      env: { isServer, isClient, isDev }
    }
  }
})

الناتج:

TEXT
// تم التنفيذ بنجاح

(1) مقارنة بين إضافات الخادم والعميل

البُعد .server.ts .client.ts .ts (عام)
تنفيذ SSR
تنفيذ العميل
واجهة برمجة المتصفح ❌ غير متاحة ✅ متاحة ⚠️ تحقق أولاً
واجهة برمجة Node.js ✅ متاحة ❌ غير متاحة ⚠️ بحسب الحالة
البيانات الخاصة ✅ آمنة ❌ قد تُكشف ⚠️ بحذر

6. ترتيب تنفيذ الإضافات

(1) قواعد الترتيب

القاعدة مثال ترتيب التنفيذ
مرتبة أبجدياً حسب اسم الملف 01-aaa.ts → 02-bbb.ts 01 يُنفَّذ أولاً
التحكم بالبادئة الرقمية 01-env.ts → 02-db.ts → 03-api.ts بالترتيب الرقمي
الخادم قبل العميل db.server.ts → stripe.client.ts الخادم أولاً أثناء SSR
إضافات nuxt.config في النهاية إضافات محقونة عبر الوحدات إضافات الإطار تُنفَّذ أولاً

(1) ▶ مثال: التحكم في ترتيب تنفيذ الإضافات

TEXT
plugins/
├── 01-runtime-env.ts       # أولاً: إعداد البيئة
├── 02-database.server.ts   # ثانياً: اتصال قاعدة البيانات
├── 03-logger.ts            # ثالثاً: تهيئة المسجّل
├── 04-stripe.client.ts     # رابعاً: تهيئة Stripe
└── 05-analytics.client.ts  # خامساً: تهيئة التحليلات

الناتج:

TEXT
تم التنفيذ بنجاح

(2) ▶ مثال: استخدام إضافات أخرى داخل إضافة

TYPESCRIPT
// plugins/05-analytics.client.ts
export default defineNuxtPlugin((nuxtApp) => {
  // الوصول إلى إضافة مسجلة مسبقاً
  const { $logger } = nuxtApp

  $logger.info('Analytics plugin initialized')

  const analytics = {
    track(event: string, data?: any) {
      $logger.info(`Track: ${event}`, data)
      // إرسال إلى خدمة التحليلات
    },
    pageView(path: string) {
      $logger.info(`Page view: ${path}`)
    }
  }

  // تتبع مشاهدات الصفحات تلقائياً
  nuxtApp.hook('page:finish', () => {
    analytics.pageView(window.location.pathname)
  })

  return { provide: { analytics } }
})

الناتج:

TEXT
// تم التنفيذ بنجاح

7. مثال شامل: نظام إضافات MegaShop

TYPESCRIPT
// plugins/01-config.ts - مساعد تكوين وقت التشغيل
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  return {
    provide: {
      config: {
        apiBase: config.public.apiBase,
        currency: 'USD',
        locale: 'en-US',
        maxCartItems: 99
      }
    }
  }
})
TYPESCRIPT
// plugins/02-logger.ts - تسجيل منظم
export default defineNuxtPlugin(() => {
  const logger = {
    info(msg: string, ctx?: Record<string, any>) {
      console.log(JSON.stringify({ level: 'info', msg, ctx, ts: Date.now() }))
    },
    error(msg: string, ctx?: Record<string, any>) {
      console.error(JSON.stringify({ level: 'error', msg, ctx, ts: Date.now() }))
    }
  }
  return { provide: { logger } }
})
TYPESCRIPT
// plugins/03-stripe.client.ts - SDK الدفع
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  const stripe = window.Stripe(config.public.stripePublishableKey)
  return { provide: { stripe } }
})
TYPESCRIPT
// plugins/04-notification.client.ts - إشعارات المتصفح
export default defineNuxtPlugin(() => {
  const notify = {
    send(title: string, body: string) {
      if ('Notification' in window && Notification.permission === 'granted') {
        new Notification(title, { body })
      }
    }
  }
  return { provide: { notify } }
})

❓ أسئلة شائعة

س ما الفرق بين الإضافة و Composable؟
ج الإضافة تعمل تلقائياً مرة عند بدء التطبيق وتُستخدم لتهيئة مكتبات الطرف الثالث. Composable يُستدعى عند الحاجة داخل مكوّن ويُستخدم لإعادة استخدام المنطق. Stripe يستخدم إضافة للتهيئة و Composable لتنسيق الأسعار.
س لماذا تبدأ أسماء الخدمات المحقونة ببادئة $؟
ج توصي Nuxt بإضافة بادئة $ للخدمات المحقونة ($stripe/$logger) لتجنب التعارض مع المتغيرات داخل المكونات. هذا اصطلاح تسمية في Nuxt، وليس متطلباً.
س هل يمكنني استخدام Pinia Store في إضافة؟
ج نعم، لكن انتبه للتوقيت. يتم تهيئة Pinia قبل الإضافة، لذا يمكنك استخدام useXxxStore() داخل الإضافة. لكن يُنصح بإدارة Store داخل Composable واستخدام الإضافة فقط للتهيئة.
س متى تُنفَّذ إضافات العميل؟
ج تُنفَّذ أثناء إرواء العميل. إضافات .client.ts لا تعمل أثناء مرحلة SSR. عند تحميل صفحة لأول مرة، تُنفَّذ إضافات العميل بعد اكتمال الإرواء.
س هل يمكنني تسجيل إضافات ديناميكياً؟
ج غير موصى به. يتم حل إضافات Nuxt أثناء البناء وتُنفَّذ تلقائياً عند التشغيل. إذا كنت بحاجة لتأخير التهيئة، استخدم التحميل الكسول داخل الإضافة (مثل الاستيراد الديناميكي).
س ماذا أفعل إذا كان SDK طرف ثالث (مثل Stripe) يُحمَّل ببطء؟
ج حمّل SDK ديناميكياً في إضافة .client.ts لتجنب حظر الإرواء: const Stripe = (await import('@stripe/stripe-js')).default. أو استخدم وسم <script> للتحميل المسبق في قسم <head>.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): أنشئ إضافة تسجيل واستخدم useNuxtApp().$logger.info() لتسجيل الرسائل داخل مكوّن.
  2. تمرين متقدم (الصعوبة: ⭐⭐): أنشئ إضافة Stripe للعميل وإضافة قاعدة بيانات للخادم، وتحقق من أنهما تعملان في بيئات مختلفة.
  3. تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ إضافة تحليلات تستخدم nuxtApp.hook('page:finish') لتتبع مشاهدات الصفحات تلقائياً وتعمل مع إضافة التسجيل لتسجيل البيانات.

---|

Web-Tutorial.com

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

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

100%