404 Not Found

404 Not Found


nginx

محرك الخادم Nitro

يحتاج Charlie إلى إضافة طبقة تخزين مؤقت إلى MegaShop—استعلامات قاعدة البيانات المتكررة على صفحة عرض ملايين المنتجات بطيئة جدًا. اكتشف Bob أن طرق التخزين تختلف بين بيئات النشر (Node، Docker، Vercel). يوفر محرك Nitro طبقة تجريد تخزين موحدة وواجهة تخزين مؤقت، مما يسمح لقاعدة كود واحدة بالعمل في جميع البيئات.

1. ما ستتعلمه


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

(1) نقطة الألم: استعلامات ملايين المنتجات ترهق قاعدة البيانات

تقوم صفحة تفاصيل منتج MegaShop بتنفيذ 2000 استعلام قاعدة بيانات في الثانية بزمن استجابة 500 مللي ثانية. أضاف Bob تخزينًا مؤقتًا لـ Redis، لكن الكود مقترن بإحكام مع بيئة النشر—يستخدم تخزينًا مؤقتًا قائمًا على الملفات محليًا وRedis في الإنتاج، مما يتطلب مجموعتين منفصلتين من تغييرات الكود.

(2) حلول لطبقة تجريد تخزين Nitro

تتيح واجهة التخزين الموحدة useStorage() من Nitro تبديل البرامج دون تغيير الكود:

TYPESCRIPT
// نفس الكود، برنامج مختلف بناءً على الإعداد
const storage = useStorage('products')
await storage.setItem('product:123', productData)

(3) الفوائد: واجهة موحدة + زمن استجابة 5 مللي ثانية

تم توحيد كود طبقة التخزين: يُستخدم الذاكرة/نظام الملفات للتطوير المحلي، وRedis/KV في الإنتاج. عند حدوث إصابة تخزين مؤقت للمنتج، يكون زمن الاستجابة 5 مللي ثانية، وتنخفض استعلامات قاعدة البيانات بنسبة 95%.


3. بنية Nitro

(1) نظرة عامة على بنية Nitro

100%
graph TB
    A[Nitro Engine] --> B[Rollup Build]
    A --> C[H3 HTTP Framework]
    A --> D[Storage Abstraction]
    A --> E[Cache System]
    A --> F[Hook System]
    A --> G[Multi-Preset Deploy]

    B --> B1[Server bundle]
    B --> B2[Tree-shaking unused code]

    C --> C1[defineEventHandler]
    C --> C2[Router / Middleware]

    D --> D1[Memory Driver]
    D --> D2[FileSystem Driver]
    D --> D3[Redis Driver]
    D --> D4[Cloud KV Driver]

    E --> E1[cachedEventHandler]
    E --> E2[defineCachedFunction]

    G --> G1[Node Server]
    G --> G2[Vercel / Cloudflare]
    G --> G3[Docker / Lambda]

(2) مقارنة إعدادات Nitro

الإعدادات بيئة التشغيل برنامج التخزين طريقة النشر
node-server Node.js fs/memory/redis PM2/Docker
Vercel Vercel Serverless Vercel KV git push
vercel-edge Vercel Edge Edge KV git push
cloudflare-pages Cloudflare Workers KV/R2 wrangler deploy
netlify Netlify Functions Netlify Blobs git push
bun Bun runtime fs/memory bun run

(1) ▶ مثال: تكوين إعداد Nitro

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: process.env.DEPLOY_TARGET || 'node-server',
    compressPublicAssets: true,
    storage: {
      // التطوير: برنامج الذاكرة
      cache: { driver: 'memory' },
      // الإنتاج: مكوّن عبر البيئة
      products: { driver: process.env.STORAGE_DRIVER || 'memory' }
    }
  }
})

الناتج:

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

4. طبقة تجريد التخزين

(1) واجهة useStorage API

الطريقة الوصف مثال
setItem(key, value) كتابة await storage.setItem('product:1', data)
getItem(key) قراءة await storage.getItem('product:1')
removeItem(key) حذف await storage.removeItem('product:1')
getKeys(base) قائمة المفاتيح await storage.getKeys('product:')
hasItem(key) وجود await storage.hasItem('product:1')
clear(base) مسح await storage.clear('product:')

(1) ▶ مثال: تخزين مؤقت للمنتجات

TYPESCRIPT
// server/api/products/[id].get.ts - مع تخزين مؤقت
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')
  const storage = useStorage('products')

  // محاولة التخزين المؤقت أولاً
  const cached = await storage.getItem(`product:${id}`)
  if (cached) return cached

  // عدم الإصابة: جلب من قاعدة البيانات
  const product = await fetchProductFromDB(Number(id))
  if (!product) {
    throw createError({ statusCode: 404, message: 'Product not found' })
  }

  // تخزين في التخزين المؤقت مع TTL (ساعة واحدة)
  await storage.setItem(`product:${id}`, product, { ttl: 3600 })

  return product
})

الناتج:

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

(2) ▶ مثال: تكوين تخزين Redis

TYPESCRIPT
// nuxt.config.ts - تخزين Redis للإنتاج
export default defineNuxtConfig({
  nitro: {
    storage: {
      products: {
        driver: 'redis',
        url: process.env.REDIS_URL || 'redis://localhost:6379',
        prefix: 'megashop:products:'
      },
      cache: {
        driver: 'redis',
        url: process.env.REDIS_URL || 'redis://localhost:6379',
        prefix: 'megashop:cache:'
      }
    }
  }
})

الناتج:

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

(2) مقارنة برامج التخزين

البرنامج السرعة الديمومة موزّع حالات الاستخدام
memory ⚡⚡⚡ ❌ تُفقد عند إعادة التشغيل التطوير/الاختبار
fs ⚡⚡ ✅ محلي نشر مستقل
redis ⚡⚡ كتلة الإنتاج
cloudflare-kv Cloudflare
vercel-kv Vercel

5. واجهة التخزين المؤقت

(1) ▶ مثال: cachedEventHandler

TYPESCRIPT
// server/api/products/featured.get.ts
export default cachedEventHandler(
  async () => {
    // نتيجة هذا المعالج مُخزّنة مؤقتًا
    const products = await $fetch('/api/internal/products/featured')
    return products
  },
  {
    maxAge: 60 * 60,           // تخزين مؤقت لساعة واحدة
    swr: true,                  // تقديم القديم أثناء إعادة التحقق
    staleMaxAge: 60 * 60 * 4,  // صالح لـ 4 ساعات
    getKey: () => 'featured-products',
    varies: ['Accept-Language'] // تخزين مؤقت لكل لغة
  }
)

الناتج:

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

(2) ▶ مثال: defineCachedFunction

TYPESCRIPT
// server/utils/cachedProduct.ts
export const getCachedProduct = defineCachedFunction(
  async (id: number) => {
    return await fetchProductFromDB(id)
  },
  {
    maxAge: 60 * 60,
    swr: true,
    getKey: (id) => `product:${id}`,
    name: 'cachedProduct'
  }
)

// الاستخدام في معالج API
// server/api/products/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = Number(getRouterParam(event, 'id'))
  return await getCachedProduct(id)
})

الناتج:

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

6. خطافات الأحداث

(1) خطافات دورة حياة Nitro

الخطاف شرط التفعيل الغرض
close إيقاف الخدمة تحرير الموارد
error خطأ غير معالج الإبلاغ عن الأخطاء
request بدء الطلب تسجيل/مصادقة
response إرسال الاستجابة إحصائيات الأداء
beforeResponse قبل الاستجابة تعديل الاستجابة

(1) ▶ مثال: خطاف حدث تحديث المخزون

TYPESCRIPT
// server/plugins/stock.ts
export default defineNitroPlugin((nitroApp) => {
  // الاستماع لأحداث تحديث المخزون
  nitroApp.hooks.hook('request', async (event) => {
    const url = getRequestURL(event)
    if (url.pathname === '/api/orders' && getMethod(event) === 'POST') {
      // قبل الطلب: التحقق من توفر المخزون
      const body = await readBody(event)
      const outOfStock = await checkStockAvailability(body.items)
      if (outOfStock.length > 0) {
        throw createError({
          statusCode: 400,
          message: `Items out of stock: ${outOfStock.join(', ')}`
        })
      }
    }
  })

  // بعد الطلب: إبطال التخزين المؤقت للمنتج
  nitroApp.hooks.hook('afterResponse', async (event) => {
    const url = getRequestURL(event)
    if (url.pathname === '/api/orders' && getMethod(event) === 'POST') {
      const body = await readBody(event)
      const storage = useStorage('products')
      // إبطال التخزين المؤقت للمنتجات
      for (const item of body.items) {
        await storage.removeItem(`product:${item.productId}`)
      }
    }
  })
})

الناتج:

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

7. مثال شامل: نظام تخزين مؤقت لمنتجات MegaShop

TYPESCRIPT
// nuxt.config.ts - تكوين Nitro للإنتاج
export default defineNuxtConfig({
  nitro: {
    preset: process.env.DEPLOY_TARGET || 'node-server',
    compressPublicAssets: true,
    storage: {
      products: {
        driver: process.env.NODE_ENV === 'production' ? 'redis' : 'memory',
        url: process.env.REDIS_URL
      }
    },
    cache: {
      pages: ['/products/', '/categories/']
    }
  }
})
TYPESCRIPT
// server/api/products/[id].get.ts - استراتيجية تخزين مؤقت كاملة
export default cachedEventHandler(
  async (event) => {
    const id = Number(getRouterParam(event, 'id'))
    const product = await fetchProductFromDB(id)

    if (!product) {
      throw createError({ statusCode: 404, message: 'Product not found' })
    }

    return product
  },
  {
    maxAge: 60 * 60,         // تخزين مؤقت لساعة واحدة
    swr: true,                // تقديم القديم أثناء إعادة التحقق
    staleMaxAge: 60 * 60 * 24, // صالح لـ 24 ساعة
    getKey: (event) => `product:${getRouterParam(event, 'id')}`,
    varies: ['Accept-Language']
  }
)
TYPESCRIPT
// server/api/admin/invalidate-cache.post.ts - إبطال يدوي للتخزين المؤقت
export default defineEventHandler(async (event) => {
  const { productId, scope } = await readBody(event)
  const storage = useStorage('products')

  if (scope === 'all') {
    // إبطال جميع تخزينات المنتجات المؤقتة
    const keys = await storage.getKeys()
    for (const key of keys) {
      await storage.removeItem(key)
    }
    return { message: 'All caches invalidated' }
  }

  if (productId) {
    await storage.removeItem(`product:${productId}`)
    return { message: `Cache invalidated for product ${productId}` }
  }

  throw createError({ statusCode: 400, message: 'Specify productId or scope=all' }
})

❓ أسئلة شائعة

س ما العلاقة بين Nitro وNuxt؟
ج Nitro هو محرك جانب الخادم لـ Nuxt 3، المسؤول عن البناء والتوجيه والتخزين والتخزين المؤقت والنشر. Nuxt 3 = Vue 3 + Vite (الواجهة) + Nitro (الخلفية).
س هل تُفقد البيانات بعد إعادة التشغيل عند استخدام برنامج الذاكرة useStorage؟
ج نعم. بينما يكون برنامج الذاكرة مناسبًا في بيئة التطوير، يجب استخدام برنامج استمراري مثل Redis أو FS في بيئة الإنتاج. لتكوين البرنامج، ما عليك سوى تعديل nuxt.config.ts—لا حاجة لتغيير الكود.
س ما الفرق بين cachedEventHandler وrouteRules في SWR؟
ج routeRules في SWR يخزن HTML الصفحة بالكامل مؤقتًا، بينما cachedEventHandler يخزن بيانات استجابة API مؤقتًا. استخدم routeRules على مستوى الصفحة وcachedEventHandler على مستوى API.
س ما الفرق بين إضافات Nitro وإضافات Nuxt؟
ج إضافات Nitro (server/plugins/) تعمل على الخادم وتتفاعل مع التخزين والخطافات وقاعدة البيانات. إضافات Nuxt (الموجودة في plugins/) تعمل ضمن تطبيق Vue وتتعامل مع المكونات وComposables وSDKs الطرف الثالث.
س كيف أراقب معدل إصابة التخزين المؤقت لـ Nitro؟
ج سجّل إصابات التخزين المؤقت وأخطاءه في خطافات الطلب/الاستجابة. في الإنتاج، استخدم أداة APM (Sentry/Datadog) للمراقبة، أو أنشئ نقطة نهاية مقاييس خاصة بك.
س كيف أختار بين Cloudflare KV وRedis؟
ج استخدم KV لعمليات نشر Cloudflare (تكامل أصلي، توزيع عالمي)، وRedis لعمليات نشر Node.js/Vercel (زمن استجابة منخفض، هياكل بيانات غنية). يستخدم MegaShop useStorage لتوفير تجريد موحد عبر بيئات متعددة.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): تكوين برنامج ذاكرة useStorage لتنفيذ عمليات قراءة وكتابة بسيطة لتخزين مؤقت لبيانات المنتجات
  2. تمرين متقدم (الصعوبة: ⭐⭐): استخدام cachedEventHandler لتنفيذ تخزين مؤقت لواجهة قائمة المنتجات، والتحقق من أن الاستجابة أسرع عند إصابة التخزين المؤقت.
  3. تحدي (الصعوبة: ⭐⭐⭐): تنفيذ آلية إبطال تخزين مؤقت كاملة—إبطال التخزين المؤقت للمنتجات ذات الصلة تلقائيًا عند إنشاء طلب؛ يجب أن تسمح واجهة API يدوية بمسح التخزين المؤقت حسب معرف المنتج أو مسح التخزين المؤقت بالكامل.

---|

Web-Tutorial.com

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

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

100%