استخدام قواعد البيانات من الملفات المسطحة في تطبيقات Nuxt الثابتة: دليل شامل

دقائق القراءة: 7

مقدمة: تعزيز الأداء والمرونة في تطبيقات Nuxt الثابتة

إن تحويل تطبيق Nuxt الخاص بك إلى تطبيق ثابت (Static Web App) يمكن أن يوفر عليك الوقت والمال الذي قد تنفقه على إعداد تطبيق يعتمد على العرض من جانب الخادم (Server-Rendered App). علاوة على ذلك، قد يقدم هذا النهج أداءً فائقًا. ولكن ماذا لو كان تطبيقك يحتاج إلى بيانات ديناميكية؟ الحل الأكثر شيوعًا هو إعداد واجهة برمجة تطبيقات (API) تعمل جنبًا إلى جنب مع تطبيقك الثابت لتوفير البيانات الديناميكية عبر تقنية AJAX.

في هذا المقال، سأعرض لك بنية معمارية بديلة وممكنة: استخدام قاعدة بيانات من الملفات المسطحة (Flat-File Database). يمكن لهذه البنية أن توفر عليك عناء إعداد واجهة برمجة تطبيقات وتقدم أداءً متفوقًا في سيناريوهات معينة، مما يجعلها خيارًا جذابًا للمطورين الباحثين عن حلول بسيطة وفعالة.

ما هي قاعدة البيانات من الملفات المسطحة؟

تعني عبارة "قاعدة بيانات من الملفات المسطحة" بنية قاعدة بيانات يتم فيها تخزين البيانات في ملف نصي بسيط، بدلاً من استخدام برمجيات قواعد بيانات معقدة مثل MySQL أو MongoDB. في سياق تطبيق Nuxt، يمكن أن يكون هذا الملف عبارة عن ملف JSON يوضع في دليل الملفات الثابتة (static files directory) ويتم نشره جنبًا إلى جنب مع ملفات الترميز (markup files).

أثناء وقت التشغيل (runtime)، يتم تحميل ملف JSON بواسطة تطبيق Nuxt. وبمجرد تحليل البيانات كبيانات JavaScript، يمكن استخدامها لتشغيل التطبيق وتوفير المحتوى الديناميكي.

لماذا نستخدم قاعدة بيانات من الملفات المسطحة؟

تتمتع قواعد البيانات من الملفات المسطحة بمزايا واضحة نظرًا لبساطتها وتكاليفها التشغيلية المنخفضة (low overhead). ومع ذلك، فهي عادةً ما تكون أقل أمانًا ولا تقدم فوائد الأداء التي توفرها برمجيات قواعد البيانات التقليدية، ولهذا السبب نادرًا ما تُستخدم في التطبيقات الكبيرة والمعقدة.

ولكن في سياق تطبيقات Nuxt، تتمتع بميزة كبيرة أخرى: يمكن تخزينها والوصول إليها من خلال استضافة ثابتة (static hosting). قد يكون لاستخدام قاعدة بيانات من الملفات المسطحة ميزة أداء على خدمة API، والتي ستتحمل تكلفة زمن انتقال صغيرة (latency overhead) عند معالجة الطلبات بواسطة الخادم.

ومع ذلك، لن تكون قواعد البيانات من الملفات المسطحة مناسبة دائمًا للاستخدام، حيث إنها لا توفر أي أمان وهي للقراءة فقط (read-only) أثناء الإنتاج. هذا يعني أنك ستحتاج إلى إعادة بناء الموقع في أي وقت تريد فيه كتابة بيانات جديدة. نوع البيانات الذي يعتبر مرشحًا جيدًا للتخزين والاسترجاع من الملفات المسطحة هو البيانات الوصفية (meta data).

على سبيل المثال، في مدونة Vue.js Developers، التي تم بناؤها باستخدام Nuxt، تُستخدم قاعدة بيانات من الملفات المسطحة لتخزين البيانات الوصفية حول المنشورات المنشورة. يتيح ذلك الوصول بسهولة إلى تلك البيانات عبر الموقع، على سبيل المثال في الصفحة الرئيسية حيث يتم عرض أحدث مقالات المدونة، وفي صفحة المواضيع التي تفهرس المنشورات بناءً على علامات المواضيع المطبقة (كما هو موضح أدناه).

عرض لصفحة رئيسية ومدونة لموقع Vue.js Developers توضح استخدام البيانات الوصفية من الملفات المسطحة لعرض أحدث المقالات والمواضيع.

تطبيق بنية قاعدة البيانات من الملفات المسطحة في Nuxt

الآن دعنا نرى كيفية تطبيق بنية قاعدة البيانات من الملفات المسطحة في موقع Nuxt الخاص بك. لنفترض أننا نريد إنشاء صفحة رئيسية لمدونة تعرض أحدث المقالات المنشورة، على غرار مدونة Vue.js Developers. سنبدأ بالنظر في كيفية استخدام البيانات المستمدة من الملفات المسطحة في الصفحة، ثم نعود خطوة بخطوة لنرى كيف تعمل البنية بأكملها.

استخدام البيانات من الملفات المسطحة في صفحة

في مكون الصفحة الرئيسية لدينا، pages/index.vue، سنقوم باستيراد الدالة getArticleSummaries من وحدة JavaScript التي سيتم إنشاؤها قريبًا، وهي flatFileDb. ستعيد هذه الدالة وعدًا (Promise) يحتوي على بيانات ملخص المقال جاهزة للاستخدام في الصفحة. يمكنك، بالطبع، استخدام هذه البيانات في وقت البناء (build time) عبر الدالة asyncData، وفي وقت التشغيل (run time) عبر الخطاف (hook) المسمى created.

pages/index.vue:

 const { getArticleSummaries } = "@/assets/js/flatFileDb"; 

 export default {
   data: () => ({
     articleSummaries: []
   }),

   async asyncData() {
     const articleSummaries = await getArticleSummaries();
     return { articleSummaries }
   },

   async created() {
     this.articleSummaries = await getArticleSummaries();
   }
 }

لاحظ أن هيكل البيانات الذي سنحصل عليه من getArticleSummaries سيكون عبارة عن مصفوفة من الكائنات (array of objects) على النحو التالي:

[
  {
    title: "...",
    description: "...",
    published: "...",
    ...
  },
  ...
]

ملاحظة: إذا كان لديك كيانات متعددة (على سبيل المثال، بالإضافة إلى المقالات، تقوم بتخزين معلومات حول مقاطع الفيديو أيضًا)، فسيكون لكل منها ملفها المسطح الخاص بها وطريقة الاسترجاع الخاصة بها في التطبيق، مثل getVideoSummaries.

وحدة قاعدة البيانات من الملفات المسطحة

لقد رأينا أعلاه أن الدالة getArticleSummaries تم استيرادها من الوحدة flatFileDb. دعنا نرى كيف يمكننا تطبيق ذلك. سيتم تضمين قاعدة البيانات من الملفات المسطحة الخاصة بنا في ملفاتنا الثابتة ويجب أن تكون ملف JSON نظرًا لسهولة تحليلها كبيانات JavaScript صالحة. سنقوم بتضمين ملف JSON هذا باستخدام استيراد ديناميكي (dynamic import).

تم تصميم هذه الميزة لاستيراد وحدات JavaScript، ولكنها تعمل مع ملفات JSON مباشرةً مع Webpack. ومن الملائم أن تحصل على ملف JSON محللًا بالفعل كـ JavaScript. من المهم استدعاء الاستيراد الديناميكي داخل كتلة try/catch لمنع تعطل التطبيق إذا كان الملف مفقودًا أو فشل تحليل JSON.

قبل أن نعيد البيانات إلى المكون المستهلك (consuming component)، نحتاج إلى "فك تشفيرها" (decode) باستخدام طريقة مخصصة أخرى تُدعى decodeArticleSummaries. سأشرح ذلك بعد قليل. أخيرًا، لاحظ أن ملف JSON لا يحتوي على تصدير افتراضي (default export)، لذلك ستحتاج إلى الوصول إلى الخاصية default لوحدة قاعدة البيانات للوصول إلى البيانات.

assets/js/flatFileDb.js:

 import { decodeArticleSummaries } from "./dbDecoders";

 const getArticleSummaries = async () => {
   try {
     const db = await import(`@/static/article-summaries.json`);
     return decodeArticleSummaries(db.default);
   } catch (err) {
     console.log(err);
     return [];
   }
 };

 export { getArticleSummaries };

فك تشفير قاعدة البيانات (Decoding the Database)

لقد ذكرت أعلاه أن البيانات المقدمة إلى المكون ستبدو كالتالي:

{
  title: "...",
  description: "...",
  published: "...",
  // etc
}

ومع ذلك، لا ينبغي تخزينها في قاعدة البيانات بهذه الطريقة لأن أسماء الخصائص طويلة بشكل غير ضروري. للحفاظ على الملف المسطح نحيفًا قدر الإمكان، يجب علينا "تشفير" (encode) كل مفتاح عند إنشاء قاعدة البيانات. ثم يجب علينا فك تشفيرها قبل أن تستهلكها المكونات بحيث تكون أسماؤها الكاملة متاحة للمطور.

لنفترض أننا نجعل "title" تصبح "t"، و "description" تصبح "d"، و "published" تصبح "p". في قاعدة بيانات كبيرة، يمكن أن يقلل هذا التحويل حجم الملف بعدة بايتات، مما يساهم في تحسين الأداء.

assets/js/dbDecode.js:

 const decodeArticleSummaries = db => {
   return db.map(
     article => ({
       title: article.t,
       description: article.d,
       published: article.p
       // etc
     })
   );
 }

 export { decodeArticleSummaries };

توليد قاعدة البيانات من الملفات المسطحة

الآن رأينا كيف يتم استهلاك قاعدة البيانات من الملفات المسطحة في وقت التشغيل. ولكن كيف يتم إنشاؤها؟ يمكنك إنشاء قاعدة بيانات من الملفات المسطحة يدويًا، ولكن عادةً ما ترغب في توليدها في وقت البناء (build time) باستخدام نص برمجي (script) مبني على Node.js.

في مثالنا، سنرغب في إنشاء نص برمجي يستخرج البيانات الوصفية لكل مقال ويخزنها كملف static/article-summaries.json. لنفترض أن المقالات مخزنة بتنسيق Markdown وموجودة في دليل "articles" في جذر المشروع.

تفاصيل النص البرمجي ستكون خاصة بتطبيقك، لذلك سأقدم لك فقط رمزًا زائفًا (pseudo code) لتوصيل الفكرة الأساسية.

scripts/generateDb.js:

 const fs = require("fs");
 const frontmatterExtractor = require("./frontmatterExtractor");
 const encodeArticleSummaries = require("./encodeArticleSummaries");

 module.exports = async () => {
   // Load article files
   const articles = await fs.readdir("/articles", (err, filePaths) => {
     // Create the database by reading each file
     const db = filePaths.map(async path => {
       const file = await fs.readFile(path);
       // Extract the meta data
       return frontmatterExtractor(file);
     });

     // Encode the data
     const encoded = encodeArticleSummaries(db);

     // Write the database object to a JSON file
     await fs.writeFile(
       "/static/article-summaries.json",
       JSON.stringify(encoded)
     );
   });
 };

تشغيل نص توليد قاعدة البيانات قبل بناء الموقع

الآن بعد أن أصبح لدينا نص برمجي لتوليد قاعدة البيانات، دعنا نجعله يعمل قبل عمليات البناء (build) أو التوليد (generate) التي ستحتاج إلى استهلاكها. للقيام بذلك، سنقوم بإدراجه في أوامر NPM في ملف package.json. لاحظ أنه باستخدام العامل &&، يمكننا ضمان عدم بدء عملية Nuxt حتى يكتمل النص البرمجي للمولد.

package.json:

{
  ...
  "scripts": {
    ...
    "build": "node scripts/generateDb && nuxt build",
    "generate": "node scripts/generateDb && nuxt generate",
    ...
  }
  ...
}

في بيئة التطوير (development)، أجد أنه من الأسهل توليد قاعدة البيانات يدويًا عبر سطر الأوامر كلما احتجت إلى تحديثها:

$ node scripts/generateDb

قراءات إضافية

هذه هي البنية الأساسية المشروحة. إليك بعض المقالات الأخرى لتعلم المزيد:

الخلاصة التقنية

توفر بنية قاعدة البيانات من الملفات المسطحة حلاً أنيقًا وفعالًا لتطبيقات Nuxt الثابتة التي تتطلب بيانات ديناميكية ولكن لا تبرر تعقيد إعداد خادم API كامل. إنها مثالية للبيانات الوصفية (metadata) التي لا تتغير كثيرًا أو تتطلب تحديثات في الوقت الفعلي، مثل ملخصات المقالات أو قائمة المنتجات الثابتة. على الرغم من قيودها المتعلقة بالأمان وقابلية الكتابة في الإنتاج، فإن بساطتها، وانخفاض تكاليفها التشغيلية، ومزايا الأداء المحتملة تجعلها خيارًا ممتازًا للمواقع التي تركز على المحتوى والمبنية على فلسفة JAMstack. يكمن مفتاح النجاح في إدارة دورة حياة البيانات بشكل فعال من خلال نصوص برمجية قوية لتوليد البيانات في وقت البناء، مما يضمن تحديث المحتوى مع كل عملية نشر.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *