نظام الإضافات
يحتاج Charlie إلى دمج Stripe Payments SDK في MegaShop ويريد أيضاً خدمة تسجيل عامة. يُحمَّل Stripe فقط من جهة العميل، بينما تُسجَّل السجلات من جهة الخادم. تقليدياً، كان هذا يتطلب استيراد وتهيئة SDK يدوياً في أماكن متعددة، لكن نظام إضافات Nuxt 3 يؤتمت العملية بالكامل—سجّلها مرة، وتكون متاحة عالمياً.
1. ما ستتعلمه
- متى يتم تسجيل وتنفيذ مجلد "plugins/" تلقائياً
- defineNuxtPlugin: استخدام
provideوinject - إضافات الخادم/العميل: التمييز بامتدادات الملفات .server.ts و .client.ts
- التحكم في ترتيب تنفيذ الإضافات
- دليل عملي لإضافة Stripe SDK للدفع وإضافة التسجيل في MegaShop
2. قصة حقيقية لمهندس معمارية
(1) نقطة الألم: تهيئة مكتبات الطرف الثالث مبعثرة في الكود
يحتاج MegaShop الخاص بـ Charlie إلى دمج مدفوعات Stripe. حمّلت Alice ملف Stripe.js في صفحة سلة التسوق، وحمّله Bob مرة أخرى في صفحة الدفع—مما أدى إلى تحميلات مكررة—وفي بعض الصفحات نُسي، مما جعل زر الدفع غير متاح. نفس الشيء ينطبق على أداة التسجيل؛ كل مكوّن يتطلب استيراد المسجّل يدوياً.
(2) حل نظام إضافات Nuxt
إضافات Nuxt 3 تعمل تلقائياً وتُحقن عبر provide للاستخدام العام:
// plugins/stripe.client.ts
export default defineNuxtPlugin(() => {
const stripe = Stripe(config.public.stripePublishableKey)
return { provide: { stripe } }
})
(3) الفوائد: سجّل مرة للوصول العام
يتم تهيئة Stripe و Logger مرة واحدة فقط داخل الإضافة؛ جميع المكونات تصل عبر useNuxtApp().$stripe، مما يلغي الحاجة للتحميل المكرر أو الاستيراد اليدوي.
3. التسجيل التلقائي للإضافات
(1) توقيت تسجيل وتنفيذ الإضافات
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) ▶ مثال: إضافة تسجيل عامة
// 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
}
}
})
الناتج:
// تم التنفيذ بنجاح
4. شرح تفصيلي لـ defineNuxtPlugin
(1) آلية Provide/Inject
| الدور | API | الوصف |
|---|---|---|
| مقدم من الإضافة | provide: { xxx } |
حقن $xxx في NuxtApp |
| استخدام المكوّن | useNuxtApp().$xxx |
الوصول للخدمة المحقونة |
| استخدام Composable | useNuxtApp().$xxx |
الوصول إلى Composable |
(1) ▶ مثال: إضافة دفع Stripe (من جهة العميل)
// plugins/stripe.client.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const stripe = window.Stripe(config.public.stripePublishableKey)
return {
provide: {
stripe
}
}
})
الناتج:
// تم التنفيذ بنجاح
(2) ▶ مثال: استخدام إضافة في مكوّن
<!-- 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>
الناتج:
// تم التنفيذ بنجاح
(3) ▶ مثال: إضافة قاعدة بيانات من جهة الخادم
// plugins/database.server.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
// يعمل فقط على الخادم - اتصال قاعدة البيانات بالمفتاح الخاص
const db = createDatabaseConnection(config.databaseUrl)
return {
provide: {
db
}
}
})
الناتج:
// تم التنفيذ بنجاح
5. التمييز بين إضافات الخادم والعميل
(1) ▶ مثال: إضافة الإشعارات (من جهة العميل، باستخدام واجهات برمجة المتصفح)
// 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 } } }
})
الناتج:
// تم التنفيذ بنجاح
(2) ▶ مثال: إضافة كشف البيئة (عامة)
// 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 }
}
}
})
الناتج:
// تم التنفيذ بنجاح
(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) ▶ مثال: التحكم في ترتيب تنفيذ الإضافات
plugins/
├── 01-runtime-env.ts # أولاً: إعداد البيئة
├── 02-database.server.ts # ثانياً: اتصال قاعدة البيانات
├── 03-logger.ts # ثالثاً: تهيئة المسجّل
├── 04-stripe.client.ts # رابعاً: تهيئة Stripe
└── 05-analytics.client.ts # خامساً: تهيئة التحليلات
الناتج:
تم التنفيذ بنجاح
(2) ▶ مثال: استخدام إضافات أخرى داخل إضافة
// 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 } }
})
الناتج:
// تم التنفيذ بنجاح
7. مثال شامل: نظام إضافات MegaShop
// plugins/01-config.ts - مساعد تكوين وقت التشغيل
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
return {
provide: {
config: {
apiBase: config.public.apiBase,
currency: 'USD',
locale: 'en-US',
maxCartItems: 99
}
}
}
})
// 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 } }
})
// plugins/03-stripe.client.ts - SDK الدفع
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const stripe = window.Stripe(config.public.stripePublishableKey)
return { provide: { stripe } }
})
// 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 } }
})
❓ أسئلة شائعة
useXxxStore() داخل الإضافة. لكن يُنصح بإدارة Store داخل Composable واستخدام الإضافة فقط للتهيئة.const Stripe = (await import('@stripe/stripe-js')).default. أو استخدم وسم <script> للتحميل المسبق في قسم <head>.📖 ملخص
- مجلد
plugins/يُسجَّل تلقائياً، وتُنفَّذ الملفات بالترتيب الأبجدي لاسم الملف defineNuxtPlugin+provideيحقنان الخدمات العامة؛ المكونات تصل إليها عبرuseNuxtApp().$xxx- امتدادات الملفات .server.ts و .client.ts تميز بين بيئات التنفيذ لتجنب التعارض بين واجهات برمجة المتصفح و Node
- البادئات الرقمية تتحكم في ترتيب التنفيذ وتضمن معالجة التبعيات بشكل صحيح
- MegaShop يدمج خدمات طرف ثالث مثل مدفوعات Stripe و Logger و Notifications عبر الإضافات
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): أنشئ إضافة تسجيل واستخدم
useNuxtApp().$logger.info()لتسجيل الرسائل داخل مكوّن. - تمرين متقدم (الصعوبة: ⭐⭐): أنشئ إضافة Stripe للعميل وإضافة قاعدة بيانات للخادم، وتحقق من أنهما تعملان في بيئات مختلفة.
- تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ إضافة تحليلات تستخدم
nuxtApp.hook('page:finish')لتتبع مشاهدات الصفحات تلقائياً وتعمل مع إضافة التسجيل لتسجيل البيانات.
---|



