تقسيم المباني الضخمة باستخدام Netlify و Next.js

نشرت: 2022-03-10
ملخص سريع ↬ يعد Static Generation رائعًا للأداء - حتى يصبح التطبيق كبيرًا جدًا وتنتهي أوقات البناء. اليوم ، سنلقي نظرة على كيف يمكن لمنشئي Netlify الجدد عند الطلب إصلاح ذلك. بالإضافة إلى ذلك ، قمنا بإقرانه بالتجديد الثابت المتزايد لـ Next.js للحصول على أفضل تجربة للمستخدم والمطور. وبالطبع ، قم بقياس تلك النتائج!

واحدة من أكبر مشاكل العمل مع مواقع الويب التي تم إنشاؤها بشكل ثابت هي الإنشاءات الأبطأ بشكل تدريجي مع نمو تطبيقك. هذه مشكلة حتمية تواجه أي مكدس في مرحلة ما ويمكن أن تنطلق من نقاط مختلفة اعتمادًا على نوع المنتج الذي تعمل به.

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

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

Jamstack هندسة الخدمة العامة
هيكل الخدمة العامة Jamstack (معاينة كبيرة)

علاوة على كل ذلك ، هناك حقيقة أخرى: غالبًا ما تكون البيانات ديناميكية ، مما يعني أنه عند إنشاء تطبيقنا ونشره ، فقد يستغرق الأمر بضع ثوانٍ أو بضع دقائق أو حتى ساعة. في غضون ذلك ، يستمر العالم في الدوران ، وإذا كنا نحصل على البيانات من مكان آخر ، فلا بد أن يصبح تطبيقنا قديمًا. غير مقبول! بناء مرة أخرى للتحديث!

بناء مرة واحدة ، وتحديث عند الحاجة

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

ومع ذلك ، فإن تحديد خوارزمية فرق ليس بالمهمة السهلة. لكي يستفيد المستخدم النهائي بالفعل من هذا التحسين ، هناك استراتيجيات لإلغاء ذاكرة التخزين المؤقت يجب مراعاتها. قصة قصيرة طويلة: لا نريد إبطال ذاكرة التخزين المؤقت لصفحة أو أصل لم يتغير.

جاء Next.js مع التجديد الثابت المتزايد ( ISR ). في جوهرها ، إنها طريقة للإعلان لكل مسار عن عدد المرات التي نريدها لإعادة البناء. تحت الغطاء ، فإنه يبسط الكثير من العمل على جانب الخادم. لأن كل مسار (ديناميكي أو غير ديناميكي) سيعيد بناء نفسه في إطار زمني محدد ، ويتناسب تمامًا مع بديهية Jamstack لإبطال ذاكرة التخزين المؤقت في كل بنية. فكر في الأمر على أنه عنوان max-age ولكن للمسارات في تطبيق Next.js الخاص بك.

لبدء تطبيقك ، ISR مجرد خاصية تكوين. في مكون المسار (داخل الدليل /pages ) ، انتقل إلى طريقة getStaticProps وأضف مفتاح revalidate إلى كائن الإرجاع:

 export async function getStaticProps() { const { limit, count, pokemons } = await fetchPokemonList() return { props: { limit, count, pokemons, }, revalidate: 3600 // seconds } }

سيتأكد المقتطف أعلاه من إعادة بناء صفحتي كل ساعة وجلب المزيد من البوكيمون لعرضها.

ما زلنا نحصل على الإصدارات المجمّعة بين الحين والآخر (عند إصدار نشر جديد). لكن هذا يسمح لنا بفصل المحتوى عن الكود ، من خلال نقل المحتوى إلى نظام إدارة المحتوى (CMS) يمكننا تحديث المعلومات في بضع ثوانٍ ، بغض النظر عن حجم تطبيقنا. وداعا للخطافات المطبعية لتحديث الأخطاء المطبعية!

بناة حسب الطلب

أطلقت Netlify مؤخرًا أدوات بناء عند الطلب وهي نهجها لدعم ISR لـ Next.js ، ولكنها تعمل أيضًا عبر أطر عمل بما في ذلك Eleventy و Nuxt. في الجلسة السابقة ، أثبتنا أن ISR كانت خطوة رائعة نحو تقصير أوقات البناء وعالجنا جزءًا كبيرًا من حالات الاستخدام. ومع ذلك ، كانت هناك محاذير:

  1. يبني الكامل على النشر المستمر.
    المرحلة المتزايدة تحدث فقط بعد النشر وللبيانات. لا يمكن شحن الكود بشكل تدريجي
  2. التراكيب المتزايدة هي نتاج الوقت.
    يتم إبطال ذاكرة التخزين المؤقت على أساس زمني. لذلك قد تحدث عمليات إنشاء غير ضرورية ، أو قد تستغرق التحديثات المطلوبة وقتًا أطول اعتمادًا على فترة إعادة التحقق المحددة في الكود.

تسمح البنية الأساسية الجديدة للنشر في Netlify للمطورين بإنشاء منطق لتحديد أجزاء تطبيقهم التي ستبني على النشر والأجزاء التي سيتم تأجيلها ( وكيف سيتم تأجيلها).

  • حرج
    لا يلزم اتخاذ أي إجراء. سيتم بناء كل ما تنشره على الدفع .
  • مؤجل
    لن يتم إنشاء جزء معين من التطبيق عند النشر ، وسيتم تأجيله ليتم بناؤه عند الطلب عند حدوث الطلب الأول ، ثم سيتم تخزينه مؤقتًا مثل أي مورد آخر من نوعه.

إنشاء منشئ عند الطلب

بادئ ذي بدء ، أضف حزمة netlify / وظائف باعتبارها devDependency لمشروعك:

 yarn add -D @netlify/functions

بمجرد الانتهاء من ذلك ، يكون الأمر مشابهًا تمامًا لإنشاء وظيفة Netlify جديدة. إذا لم تقم بتعيين دليل محدد لهم ، فانتقل إلى netlify/functions/ وقم بإنشاء ملف بأي اسم للمُنشئ الخاص بك.

 import type { Handler } from '@netlify/functions' import { builder } from '@netlify/functions' const myHandler: Handler = async (event, context) => { return { statusCode: 200, body: JSON.stringify({ message: 'Built on-demand! ' }), } } export const handler = builder(myHandler)

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

Next.js على Netlify

لإنشاء تطبيق Next.js على Netlify ، هناك مكونان إضافيان مهمان يجب إضافتهما للحصول على تجربة أفضل بشكل عام: Netlify Plugin Cache Next.js و Essential Next-on-Netlify. يقوم الأول بتخزين NextJS الخاص بك بشكل أكثر كفاءة وتحتاج إلى إضافته بنفسك ، بينما يقوم الأخير بإجراء بعض التعديلات الطفيفة على كيفية إنشاء بنية Next.js بحيث تتناسب بشكل أفضل مع Netlify ومتاح افتراضيًا لكل مشروع جديد يمكن لـ Netlify تحديده باستخدام Next.js.

بناة عند الطلب مع Next.js

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

حصلت Netlify على ظهرك. في بضع خطوات فقط ، يمكننا الاستفادة من القوة الكاملة لـ Jamstack في تطبيق Next.js. حان الوقت لنشمر عن سواعدنا ونجمعها معًا الآن.

تحديد مسارات معروضة مسبقًا

إذا كنت قد عملت مع الجيل الثابت داخل Next.js من قبل ، فمن المحتمل أنك سمعت عن طريقة getStaticPaths . هذه الطريقة مخصصة للمسارات الديناميكية (قوالب الصفحات التي ستعرض نطاقًا واسعًا من الصفحات). بدون الخوض في الكثير من التفاصيل حول تعقيدات هذه الطريقة ، من المهم ملاحظة أن نوع الإرجاع عبارة عن كائن به مفتاحان ، كما هو الحال في إثبات المفهوم ، سيكون هذا ملف مسار ديناميكي [بوكيمون]:

 export async function getStaticPaths() { return { paths: [], fallback: 'blocking', } }
  • paths عبارة عن array تقوم بتنفيذ جميع المسارات المطابقة لهذا المسار والتي سيتم تقديمها مسبقًا
  • يحتوي fallback على 3 قيم محتملة: الحظر أو true أو false

في حالتنا ، يحدد getStaticPaths :

  1. لن يتم تقديم أي مسارات مسبقًا ؛
  2. عندما يتم استدعاء هذا المسار ، لن نقدم نموذجًا احتياطيًا ، وسنعرض الصفحة عند الطلب ونبقي المستخدم ينتظر ، ويمنع التطبيق من فعل أي شيء آخر.

عند استخدام أدوات الإنشاء حسب الطلب ، تأكد من أن إستراتيجيتك الاحتياطية تلبي أهداف تطبيقك ، فإن مستندات Next.js الرسمية: المستندات الاحتياطية مفيدة جدًا.

قبل أداة البناء عند الطلب ، كان getStaticPaths مختلفًا قليلاً:

 export async function getStaticPaths() { const { pokemons } = await fetchPkmList() return { paths: pokemons.map(({ name }) => ({ params: { pokemon: name } })), fallback: false, } }

كنا نجمع قائمة بجميع صفحات البوكيمون التي نرغب في امتلاكها ، وقمنا بتعيين جميع كائنات pokemon إلى string فقط تحمل اسم البوكيمون ، وأعدنا إعادة توجيه الكائن { params } الذي يحمله إلى getStaticProps . تم تعيين الإجراء fallback على " false " لأنه إذا لم يكن المسار مطابقًا ، فقد أردنا أن يقوم Next.js بطرح 404: Not Found .

يمكنك التحقق من نشر كلا الإصدارين على Netlify:

  • مع On-Demand Builder: كود ، مباشر
  • تم إنشاؤه ثابتًا بالكامل: كود ، مباشر

الكود مفتوح المصدر أيضًا على Github ويمكنك بسهولة نشره بنفسك للتحقق من أوقات الإنشاء. ومن خلال قائمة الانتظار هذه ، ننتقل إلى موضوعنا التالي.

بناء تايمز

كما ذكرنا أعلاه ، فإن العرض السابق هو في الواقع إثبات للمفهوم ، ولا يوجد شيء جيد أو سيئ حقًا إذا لم نتمكن من القياس. لدراستنا الصغيرة ، ذهبت إلى PokeAPI وقررت التقاط كل البوكيمون.

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

 export const fetchPkmList = async () => { const resp = await fetch(`${API}pokemon?limit=${LIMIT}`) const { count, results, }: { count: number results: { name: string url: string }[] } = await resp.json() return { count, pokemons: results, limit: LIMIT, } }

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

إستراتيجية عدد الصفحات عدد الأصول بناء الوقت إجمالي وقت النشر
ولدت ثابتة بالكامل 1002 1005 دقيقتان و 32 ثانية 4 دقائق و 15 ثانية
بناة حسب الطلب 2 0 52 ثانية 52 ثانية

الصفحات الموجودة في تطبيق PokeDex الصغير صغيرة جدًا ، وأصول الصور قليلة جدًا ، لكن المكاسب التي تحققت في وقت النشر كبيرة جدًا. إذا كان التطبيق يحتوي على وسيط إلى كمية كبيرة من المسارات ، فمن الجدير بالتأكيد التفكير في استراتيجية ODB.

يجعل عمليات النشر أسرع وبالتالي أكثر موثوقية. تحدث نسبة الأداء فقط عند الطلب الأول ، بدءًا من الطلب اللاحق وما بعده ، سيتم تخزين الصفحة المعروضة مؤقتًا على الحافة مما يجعل الأداء مطابقًا تمامًا للأداء الثابت بالكامل.

المستقبل: العرض المستمر الموزع

في نفس اليوم ، تم الإعلان عن بناة عند الطلب وتم السماح لهم بالوصول المبكر لها ، كما نشرت Netlify أيضًا طلبها للتعليقات على العرض المستمر الموزع (DPR).

DPR هي الخطوة التالية للبناة حسب الطلب. يستفيد من عمليات الإنشاء الأسرع من خلال الاستفادة من خطوات البناء غير المتزامنة ثم تخزين الأصول مؤقتًا حتى يتم تحديثها بالفعل. لا مزيد من الإنشاءات الكاملة لموقع ويب لصفحة 10 كيلو بايت. يمكّن DPR المطورين من التحكم الكامل في إنشاء الأنظمة ونشرها من خلال التخزين المؤقت الصلب واستخدام بناة عند الطلب.

تصور هذا السيناريو: يحتوي موقع التجارة الإلكترونية على صفحات منتج تبلغ 10 آلاف صفحة ، وهذا يعني أن إنشاء التطبيق بالكامل للنشر سيستغرق حوالي ساعتين. لا نحتاج أن نجادل كم هذا مؤلم.

باستخدام DPR ، يمكننا تعيين أفضل 500 صفحة للبناء على كل عملية نشر. تكون صفحاتنا الأكثر ازدحامًا جاهزة دائمًا لمستخدمينا. لكننا متجر ، أي أن كل ثانية لها أهميتها. لذلك بالنسبة للصفحات الـ 9500 الأخرى ، يمكننا تعيين خطاف ما بعد الإنشاء لتشغيل منشئيهم - نشر باقي صفحاتنا بشكل غير متزامن والتخزين المؤقت فورًا. لم يصب أي مستخدم بأذى ، وتم تحديث موقعنا الإلكتروني بأسرع تصميم ممكن ، ثم تم تخزين كل شيء آخر غير موجود في ذاكرة التخزين المؤقت.

خاتمة

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

ما هو رأيك في العرض المستمر الموزع؟ هل جربت بناة عند الطلب في تطبيقك؟ اسمحوا لي أن أعرف المزيد في التعليقات أو اتصل بي على Twitter. أنا فضولي حقا!

مراجع

  • "دليل كامل للتجديد الثابت المتزايد (ISR) باستخدام Next.js ،" لي روبنسون
  • "إنشاءات أسرع للمواقع الكبيرة على Netlify مع بناة حسب الطلب ،" Asavari Tayal ، مدونة Netlify
  • "العرض الثابت الموزع: أسلوب Jamstack الجديد للحصول على إصدارات أسرع ،" Matt Biilmann ، مدونة Netlify
  • "العرض المستمر الموزع (DPR)" ، كاسيدي ويليامز ، جيثب