التركيب، التخزين المؤقت، والهندسة المعمارية في Next.js الحديث

VVercel
Computing/Software

Transcript

00:00:00(موسيقى مبهجة) - مرحباً بالجميع.
00:00:06اسمي أورورا.
00:00:07أنا مطورة ويب من النرويج.
00:00:09أعمل مستشارة في Crane Consulting، وأقوم حالياً بالبناء باستخدام Next.js app router في مشروعي الاستشاري الحالي.
00:00:16اليوم، سأعلمكم أنماطاً تتعلق بالتركيب والتخزين المؤقت والهندسة المعمارية في Next.js الحديثة، والتي ستساعدكم على ضمان قابلية التوسع والأداء.
00:00:24دعوني أولاً أراجع المفهوم الأساسي لهذا الحديث: العرض الثابت والديناميكي.
00:00:30لقد واجهناهما كلاهما في Next.js app router.
00:00:33يتيح لنا العرض الثابت بناء مواقع ويب أسرع لأن المحتوى المعروض مسبقاً يمكن تخزينه مؤقتاً وتوزيعه عالمياً، مما يضمن وصول المستخدمين إليه بشكل أسرع.
00:00:42على سبيل المثال، موقع Next.js Conf.
00:00:46يقلل العرض الثابت من حمل الخادم لأن المحتوى لا يحتاج إلى إنشاء لكل طلب مستخدم.
00:00:51المحتوى المعروض مسبقاً أسهل أيضاً على برامج زحف محركات البحث في الفهرسة، حيث يكون المحتوى متاحاً بالفعل عند تحميل الصفحة.
00:00:58من ناحية أخرى، يتيح العرض الديناميكي لتطبيقنا عرض بيانات في الوقت الفعلي أو يتم تحديثها بشكل متكرر.
00:01:05كما يمكننا من تقديم محتوى مخصص، مثل لوحات التحكم وملفات تعريف المستخدمين.
00:01:09على سبيل المثال، لوحة تحكم Vercel.
00:01:12باستخدام العرض الديناميكي، يمكننا الوصول إلى معلومات لا يمكن معرفتها إلا وقت الطلب.
00:01:16في هذه الحالة، من هو المستخدم الذي يصل إلى لوحة التحكم الخاصة به، وهو أنا.
00:01:20هناك واجهات برمجة تطبيقات معينة يمكن أن تتسبب في عرض الصفحة ديناميكياً.
00:01:25سيؤدي استخدام خصائص params و search params التي يتم تمريرها إلى الصفحات أو خطافاتها المكافئة إلى العرض الديناميكي.
00:01:32ومع ذلك، باستخدام params، يمكننا تحديد مجموعة من الصفحات المعروضة مسبقاً باستخدام static params عامة، ويمكننا أيضاً تخزين الصفحات مؤقتاً أثناء إنشائها بواسطة المستخدمين.
00:01:40علاوة على ذلك، ستؤدي قراءة ملفات تعريف الارتباط ورؤوس الطلبات الواردة إلى اختيار الصفحة للعرض الديناميكي.
00:01:46ولكن على عكس params، فإن محاولة التخزين المؤقت أو العرض المسبق لأي شيء باستخدام الرؤوس أو ملفات تعريف الارتباط ستتسبب في حدوث أخطاء أثناء البناء لأن تلك المعلومات لا يمكن معرفتها مسبقاً.
00:01:56أخيراً، سيؤدي استخدام fetch مع تكوين ذاكرة التخزين المؤقت للبيانات
00:01:59"no store"
00:01:59أيضاً إلى فرض العرض الديناميكي.
00:02:00هذه هي القليل، هناك عدد قليل آخر من واجهات برمجة التطبيقات التي يمكن أن تسبب العرض الديناميكي، ولكن هذه هي الأكثر شيوعاً التي نصادفها.
00:02:06في الإصدارات السابقة من Next، كانت الصفحة تُعرض إما ثابتة بالكامل أو ديناميكية بالكامل.
00:02:13واجهة برمجة تطبيقات ديناميكية واحدة على الصفحة ستجعل الصفحة بأكملها تُعرض ديناميكياً.
00:02:17على سبيل المثال، إجراء فحص مصادقة بسيط لقيمة ملف تعريف الارتباط.
00:02:20باستخدام مكونات خادم React مع Suspense، يمكننا بث المحتوى الديناميكي مثل لافتة ترحيب مخصصة أو توصيات فور جاهزيتها، وتوفير بدائل فقط باستخدام Suspense أثناء عرض المحتوى الثابت مثل رسالة إخبارية.
00:02:34ولكن بمجرد إضافة مكونات غير متزامنة متعددة على صفحة ديناميكية، مثل منتج مميز، فإنها ستعمل أيضاً وقت الطلب حتى لو لم تعتمد على واجهات برمجة تطبيقات ديناميكية.
00:02:45لذا، لتجنب حظر تحميل الصفحة الأولي، كنا سنعلق ونبث تلك المكونات أيضاً، مما يتطلب عملاً إضافياً، وإنشاء هياكل هيكلية والقلق بشأن أشياء مثل تحول التخطيط.
00:02:56ومع ذلك، غالباً ما تكون الصفحات مزيجاً من المحتوى الثابت والديناميكي.
00:03:01على سبيل المثال، تطبيق للتجارة الإلكترونية يعتمد على معلومات المستخدم بينما لا يزال يحتوي على بيانات ثابتة في الغالب.
00:03:07إن الإجبار على الاختيار بينهما، بين الثابت والديناميكي، يسبب الكثير من المعالجة الزائدة على الخادم للمحتوى الذي لا يتغير أبداً أو نادراً جداً، وهذا ليس مثالياً للأداء.
00:03:19لحل هذه المشكلة، تم الإعلان عن توجيه use cache في مؤتمر Next.js العام الماضي.
00:03:26وهذا العام، كما رأينا في الكلمة الرئيسية، أصبح متاحاً في Next.js 16.
00:03:30لذا، مع use cache، لن تُجبر الصفحات بعد الآن على العرض الثابت أو الديناميكي.
00:03:36يمكن أن تكون كلاهما.
00:03:37ولم يعد على Next.js تخمين نوع الصفحة بناءً على ما إذا كانت تصل إلى أشياء مثل params.
00:03:43كل شيء ديناميكي افتراضياً، وتسمح لنا use cache بالاشتراك صراحةً في التخزين المؤقت.
00:03:47تتيح use cache التخزين المؤقت القابل للتركيب.
00:03:51يمكننا وضع علامة على صفحة أو مكون React أو دالة كقابلة للتخزين المؤقت.
00:03:55هنا، يمكننا بالفعل تخزين مكون المنتجات المميزة مؤقتاً لأنه لا يحتاج إلى الطلب والمعالجة ولا يستخدم واجهات برمجة تطبيقات ديناميكية.
00:04:03ويمكن عرض هذه الأجزاء المخزنة مؤقتاً مسبقاً وتضمينها كجزء من الغلاف الثابت مع العرض المسبق الجزئي، مما يعني أن المنتجات المميزة متاحة الآن عند تحميل الصفحة ولا تحتاج إلى البث.
00:04:14الآن بعد أن أصبح لدينا هذه المعرفة الأساسية المهمة، دعونا نقوم بعرض توضيحي.
00:04:19تحسين قاعدة بيانات برمجية تحتوي على مشكلات شائعة غالباً ما تُصادف في تطبيقات Next.js.
00:04:24تشمل هذه المشكلات تمرير الخصائص المتعمق (prop drilling)، مما يجعل من الصعب صيانة وإعادة هيكلة الميزات، وجافاسكريبت زائدة من جانب العميل ومكونات كبيرة ذات مسؤوليات متعددة، ونقص في العرض الثابت، مما يؤدي إلى تكاليف خادم إضافية وتدهور في الأداء.
00:04:36حسناً، لنبدأ.
00:04:37فقط ثانية واحدة هنا.
00:04:50حسناً، رائع.
00:04:54هذا تطبيق بسيط جداً.
00:04:56إنه مستوحى من منصة للتجارة الإلكترونية.
00:04:59ودعوني أقوم بعروض توضيحية أولية هنا.
00:05:01لذا يمكنني تحميل هذه الصفحة.
00:05:03لدي بعض المحتوى مثل هذا المنتج المميز.
00:05:06لدي فئات مميزة، وبيانات منتجات مختلفة.
00:05:09هناك أيضاً صفحة
00:05:11"تصفح الكل"
00:05:12هنا حيث يمكنني رؤية جميع المنتجات في المنصة والتنقل بين صفحاتها.
00:05:20ثم لدينا صفحة "حول" هنا، وهي ثابتة فقط.
00:05:24يمكنني أيضاً تسجيل الدخول كمستخدم.
00:05:27وهذا سيسجل دخولي إلى حسابي.
00:05:30وأحصل أيضاً على محتوى مخصص في لوحة التحكم الخاصة بي هنا.
00:05:33مثل، على سبيل المثال، المنتجات الموصى بها أو هذه الخصومات المخصصة هنا.
00:05:38لاحظوا هنا، هناك مزيج جيد جداً.
00:05:42أوه، وهناك صفحة أخرى نسيت أن أريكم إياها.
00:05:45صفحة المنتج، الأهم.
00:05:47هنا أيضاً، يمكننا رؤية معلومات المنتج ثم حفظها إذا أردنا لمستخدمنا.
00:05:52لذا، لاحظوا أن هناك مزيجاً جيداً جداً هنا من المحتوى الثابت والديناميكي في هذا التطبيق بسبب جميع ميزاتنا التي تعتمد على المستخدم.
00:05:59دعونا نلقي نظرة على الكود أيضاً، والذي سيكون هنا.
00:06:05أنا أستخدم app router هنا، بالطبع، في Next.js 16.
00:06:08لدي جميع صفحاتي المختلفة، مثل صفحة
00:06:10"حول"
00:06:11، وصفحة
00:06:11"الكل"
00:06:12، وصفحة منتجاتنا.
00:06:13لدي أيضاً - أستخدم تقسيم الميزات هنا للحفاظ على مجلد تطبيقي نظيفاً.
00:06:17لدي مكونات واستعلامات مختلفة تتحدث إلى قاعدة بياناتي باستخدام Prisma.
00:06:23نعم، وقد أبطأت كل هذا عمداً.
00:06:25لهذا السبب لدينا مرحلة تحميل طويلة جداً، فقط لكي نتمكن من رؤية ما يحدث بسهولة أكبر.
00:06:31لذا، المشكلات الشائعة التي أردنا العمل عليها هنا والتي لدينا بالفعل في هذا التطبيق كانت تمرير الخصائص المتعمق (prop drilling)، مما يجعل من الصعب صيانة وإعادة هيكلة الميزات، وجافاسكريبت الزائدة من جانب العميل، ونقص في العرض الثابت مما يؤدي إلى تكاليف خادم إضافية وتدهور في الأداء.
00:06:47لذا، الهدف هنا من العرض التوضيحي هو ببساطة تحسين هذا التطبيق باستخدام بعض الأنماط الذكية المتعلقة بالتركيب والتخزين المؤقت والهندسة المعمارية لإصلاح تلك الميزات الشائعة وجعله أسرع وأكثر قابلية للتوسع وأسهل في الصيانة.
00:07:01فلنبدأ بذلك.
00:07:02المشكلة الأولى التي نريد إصلاحها تتعلق في الواقع بتمرير الخصائص (prop drilling).
00:07:05وهذا سيكون هنا في الصفحة.
00:07:10لاحظوا هنا، لدي هذا المتغير "logged in" في الأعلى.
00:07:15ويمكنكم رؤيتي أمرره إلى مكونين.
00:07:17لقد تم تمريره بالفعل عبر مستويات متعددة إلى هذه اللافتة الشخصية.
00:07:20لذا، سيجعل هذا من الصعب إعادة استخدام الأشياء هنا لأننا دائماً لدينا هذا الاعتماد على
00:07:25"logged in"
00:07:26للافتة الترحيب الخاصة بنا.
00:07:28لذا، مع مكونات الخادم، أفضل ممارسة هي دفع جلب البيانات إلى المكونات التي تستخدمها وحل الوعود أعمق في الشجرة.
00:07:37ولكي يتم المصادقة على هذا، طالما أنه يستخدم إما fetch أو شيئاً مثل React cache، يمكننا تكرار عدة استدعاءات له، ويمكننا ببساطة إعادة استخدامه في أي مكان نريده داخل مكوناتنا.
00:07:48لذا سيكون من الجيد تماماً إعادة استخدامه.
00:07:51لذا يمكننا الآن نقل هذا إلى القسم المخصص هنا.
00:07:54ولن نحتاج إلى هذه الخاصية بعد الآن.
00:07:57ونضعها مباشرة - أوه - هنا.
00:08:01ولن نحتاج إلى تمرير هذا بعد الآن.
00:08:04وبما أننا ننقل الآن هذا الاستدعاء غير المتزامن إلى القسم المخصص، فإننا لم نعد نحظر الصفحة.
00:08:09يمكننا المضي قدماً وتعليق هذا باستخدام Suspense بسيط هنا.
00:08:13ولن نحتاج إلى هذا البديل.
00:08:16أما بالنسبة للافتة الترحيب، أفترض أننا سنفعل الشيء نفسه.
00:08:22ولكن محاولة استخدام - الحصول على متغير أو قيمة
00:08:25"logged in"
00:08:26هنا، لا يعمل، أليس كذلك؟
00:08:27لأن هذا مكون عميل.
00:08:29لذا نحتاج إلى حل هذا بطريقة مختلفة.
00:08:30وسنستخدم نمطاً ذكياً جداً هنا لحل هذه المشكلة.
00:08:33سننتقل بالفعل إلى التخطيط ونغلف كل شيء هنا بموفر مصادقة (auth provider).
00:08:39لذا سأضع هذا حول تطبيقي بأكمله هنا وأحصل على متغير
00:08:43"logged in"
00:08:45هذا هنا.
00:08:45وبالتأكيد لا أريد حظر تخطيطي الجذري بالكامل.
00:08:48دعنا نمضي قدماً ونزيل "await" هنا.
00:08:50ونمرر هذا كـ "promise" إلى موفر المصادقة هذا.
00:08:55وهذا يمكن أن يحتوي على هذا الـ "promise" فقط.
00:08:57يمكن أن يبقى هناك ببساطة حتى نكون مستعدين لقراءته.
00:09:01لذا الآن لدينا هذا الإعداد.
00:09:03هذا يعني أنه يمكننا المضي قدماً والتخلص من هذه الخاصية أولاً.
00:09:09وسنتخلص من هذا الذي يتغلغل إلى اللافتة الشخصية.
00:09:12وسنتخلص أيضاً من تمرير الخصائص هنا أو التوقيع.
00:09:16والآن يمكننا استخدام موفر المصادقة هذا لجلب قيمة
00:09:19"logged in"
00:09:20محلياً داخل اللافتة الشخصية باستخدام
00:09:23"use auth"
00:09:23مع الموفر الذي أنشأناه للتو.
00:09:26وقراءتها باستخدام "use".
00:09:28لذا سيعمل هذا في الواقع بطريقة نحتاج فيها إلى تعليق هذا أثناء حله.
00:09:33لذا الآن قمت بوضع جلب البيانات الصغير هذا داخل اللافتة الشخصية.
00:09:37ولم أعد مضطراً لتمرير تلك الخصائص.
00:09:40وبينما يتم حل هذا، دعنا نمضي قدماً ونعلق هذا أيضاً ببديل.
00:09:44ودعنا نستخدم لافتة عامة هنا لتجنب أي تحول تراكمي غريب.
00:09:51وأخيراً، نتخلص من هذا أيضاً.
00:09:53لذا الآن هذه اللافتة الترحيبية قابلة للتركيب.
00:09:58إنها قابلة لإعادة الاستخدام.
00:09:59ليس لدينا أي خصائص أو تبعيات غريبة في الصفحة الرئيسية.
00:10:02وبما أننا قادرون على إعادة استخدام هذا بسهولة، دعنا نمضي قدماً ونضيفه أيضاً إلى صفحة المتصفح هذه هنا، والتي ستكون هنا.
00:10:11ويمكنني ببساطة المضي قدماً واستخدامه هنا دون أي تبعيات.
00:10:15لذا، من خلال هذه الأنماط، نحن قادرون على الحفاظ على بنية مكونات جيدة باستخدام React cache و React use، وجعل مكوناتنا أكثر قابلية للاستخدام والتركيب.
00:10:30حسناً.
00:10:31دعنا نتناول التحدي الشائع التالي، وهو جافاسكريبت الزائدة من جانب العميل والمكونات الكبيرة ذات المسؤوليات المتعددة.
00:10:40في الواقع، هذا موجود أيضاً في صفحة "الكل" هنا.
00:10:43ومرة أخرى، سيتعين علينا العمل على لافتة الترحيب هذه.
00:10:46إنها حالياً مكون عميل.
00:10:48والسبب في كونها مكون عميل هو أن لدي هذه الحالة البسيطة جداً
00:10:52"dismissed"
00:10:53هنا.
00:10:53يمكنني فقط النقر على هذا.
00:10:54إنه تفاعل واجهة مستخدم لطيف.
00:10:56هذا جيد.
00:10:57ما ليس جيداً جداً، هو أنني بسبب ذلك، أحول هذا المكون بأكمله إلى مكون من جانب العميل أو مكون عميل.
00:11:04وأنا حتى أستخدم swr لجلب البيانات من جانب العميل.
00:11:07لدي الآن طبقة واجهة برمجة التطبيقات هذه هنا.
00:11:08لم يعد لدي أمان النوع في بياناتي.
00:11:11نعم، هذا ليس ضرورياً.
00:11:12ونحن أيضاً نكسر مبدأ فصل الاهتمامات هنا لأننا ندمج منطق واجهة المستخدم مع البيانات.
00:11:18لذا دعنا نمضي قدماً ونستخدم نمطاً ذكياً آخر لإصلاح هذا.
00:11:21يُطلق عليه نمط الدونات.
00:11:23بشكل أساسي، ما سأفعله هو استخراج هذا إلى غلاف من جانب العميل.
00:11:27لذا دعنا ننشئ مكوناً جديداً هنا.
00:11:29ودعنا نسميه "banner container".
00:11:32وهذا سيحتوي على منطقنا التفاعلي مع توجيه "use client".
00:11:37يمكننا إنشاء التوقيع.
00:11:38يمكننا لصق كل ما كان لدينا سابقاً.
00:11:42وبدلاً من استخدام هذه اللافتات، سأضع خاصية هنا، والتي ستكون
00:11:47"children"
00:11:48.
00:11:48لهذا السبب يُطلق عليه نمط الدونات.
00:11:50نحن فقط نصنع منطق واجهة المستخدم هذا كغلاف حول محتوى معروض من الخادم، أو يمكن أن يكون محتوى معروض من الخادم.
00:11:56وبما أننا لم نعد نملك هذا الاعتماد من جانب العميل، يمكننا المضي قدماً وإزالة
00:12:00"use client"
00:12:01.
00:12:01يمكننا استخدام دالتنا غير المتزامنة
00:12:04"isAuth"
00:12:04هنا بدلاً من ذلك.
00:12:06يمكننا تحويل هذا إلى مكون خادم غير متزامن.
00:12:09يمكننا حتى استبدال جلب البيانات من جانب العميل بجلب البيانات من جانب الخادم.
00:12:11لذا دعني أمضي قدماً وأحصل على بيانات الخصم مباشرة هنا.
00:12:16بيانات الخصم.
00:12:18ونستخدم نموذجنا الذهني المعتاد كما كان من قبل مع أمان النوع.
00:12:24وهذا يعني أنه يمكنني أيضاً حذف طبقة واجهة برمجة التطبيقات هذه التي لا أرغب في العمل بها على أي حال.
00:12:29أخيراً، بالنسبة لـ
00:12:30"isLoading"
00:12:30، يمكننا ببساطة تصدير لافتة ترحيب جديدة هنا مع حاوية لافتة نمط الدونات الخاصة بنا التي تحتوي على محتوى معروض من الخادم.
00:12:38وهذا يعني أننا لم نعد بحاجة إلى "isLoading" هذا.
00:12:40لذا قمنا بشكل أساسي بإعادة هيكلة هذا الأمر برمته إلى مكون خادم واستخراج نقطة منطق واجهة المستخدم.
00:12:46ولكن ما هذا؟
00:12:48يبدو أن لدي خطأ آخر.
00:12:51هذا في الواقع بسبب Motion.
00:12:53استخدموا Motion.
00:12:54إنها مكتبة رسوم متحركة رائعة حقاً، ولكنها تتطلب توجيه
00:12:58"useClient"
00:12:59.
00:12:59ومرة أخرى، لا يتعين علينا جعل هذا
00:13:01"useClient"
00:13:02لمجرد الرسوم المتحركة.
00:13:03يمكننا إنشاء، مرة أخرى، غلاف بنمط الدونات واستخراج أغلفة لهذه الرسوم المتحركة فقط.
00:13:10وهذا يعني أننا لا نحتاج إلى تحويل أي شيء هنا إلى جانب العميل.
00:13:14وربما فاتني شيء هنا في الأسفل.
00:13:17نعم.
00:13:18ها نحن ذا.
00:13:21لذا الآن تم تحويل كل شيء هنا إلى خادم.
00:13:23لدينا نفس التفاعل.
00:13:24لا يزال لدينا منطقنا التفاعلي هنا، ولكن الآن لدينا هذه الطريقة الواحدة لجلب البيانات.
00:13:29ولدينا جافاسكريبت أقل بكثير من جانب العميل.
00:13:31في الواقع، أنا أستخدم نمط الدونات هذا بنفسي لمساعد حدود واجهة المستخدم هذا، والذي يبدو هكذا.
00:13:42هل ترون ذلك؟
00:13:43لذا هذا يوضح، مرة أخرى، ما أعنيه، أليس كذلك؟
00:13:45مع نمط الدونات، لدينا مكون العميل هذا حول مكون الخادم.
00:13:49لقد قمت أيضاً بوضع علامة على العديد من مكوناتي الأخرى باستخدام مساعد واجهة المستخدم هذا هنا.
00:13:53هنا أيضاً، لدي المزيد من مكونات الخادم.
00:13:56دعنا نمضي قدماً ونحسنها أيضاً، بما أننا أصبحنا جيدين جداً في هذا الآن.
00:14:01إنها في التذييل.
00:14:04هذه الفئات - أعني، لدي هذا المكون الجيد الذي يجلب بياناته الخاصة.
00:14:08وأردت فقط إضافة هذا "showMore"، فقط في حال أصبح طويلاً جداً.
00:14:14ومع نمط الدونات، يمكنني ببساطة تغليف مكون "showMore" هنا.
00:14:20وهذا سيحتوي على منطق واجهة المستخدم الخاص بي.
00:14:23وهو يبدو هكذا، أليس كذلك؟
00:14:27رائع جداً.
00:14:28وهذا يحتوي الآن على منطق العميل، مما يسمح لنا باستخدام الحالة.
00:14:33نحن نستخدم عدد العناصر الفرعية و "to array" لتقطيع هذا.
00:14:36والرائع هنا هو أن هذين المكونين أصبحا الآن قابلين للتركيب وإعادة الاستخدام بالكامل ويعملان معاً هكذا.
00:14:42لذا هذا هو جمال هذه الأنماط التي نتعلمها هنا حقاً.
00:14:45يمكنك استخدام هذا لأي شيء.
00:14:50أنا أستخدمه أيضاً لهذا المودال هنا.
00:14:52نعم، فقط تذكروا هذا في المرة القادمة التي تفكرون فيها بإضافة أي نوع من منطق العميل إلى مكونات الخادم الخاصة بكم.
00:14:59حسناً، نحن نعرف نمط الدونات.
00:15:01نعرف كيف نستخدمه لإنشاء هذه المكونات القابلة للتركيب وتجنب
00:15:05"plans.js"
00:15:06، لذا يمكننا الانتقال إلى المشكلة النهائية.
00:15:10دعني أمضي قدماً وأغلق هذا مرة أخرى.
00:15:13إذن، هذا سيكون بسبب نقص استراتيجيات العرض الثابت، أليس كذلك؟
00:15:18بالنظر إلى مخرجات البناء الخاصة بي، لدي في الواقع كل صفحة هنا ديناميكية.
00:15:24وهذا يعني أنه كلما قمت بتحميل شيء هنا، سيتم تشغيل هذا لكل مستخدم على حدة.
00:15:29عذراً.
00:15:30كل مستخدم يفتح هذا سيحصل على حالة التحميل هذه.
00:15:33سيؤدي ذلك إلى إهدار تكاليف الخادم، مما يجعل الأداء أسوأ.
00:15:37وهذا يعني أيضاً أن شيئاً ما داخل صفحاتي يسبب العرض الديناميكي أو يفرض العرض الديناميكي لجميع صفحاتي.
00:15:45في الواقع، إنه داخل تخطيطي الجذري.
00:15:49لا أعرف إذا كنتم قد جربتم هذا.
00:15:51إنه هنا.
00:15:53في رأسي، لدي ملف تعريف المستخدم هذا.
00:15:57وهذا، بالطبع، يستخدم ملفات تعريف الارتباط للحصول على المستخدم الحالي، وهذا يعني أن كل شيء آخر يُعرض ديناميكياً أيضاً.
00:16:02لأن الصفحات، مرة أخرى، يمكن أن تكون إما ديناميكية أو ثابتة، أليس كذلك؟
00:16:06هذه مشكلة شائعة جداً وقد تم حلها من قبل في الإصدارات السابقة من Next، لذا دعنا نرى ما يمكننا فعله.
00:16:13أحد الأشياء التي يمكننا فعلها هو إنشاء مجموعة مسارات وتقسيم تطبيقنا إلى أقسام ثابتة وديناميكية، مما سيسمح لي باستخراج صفحة
00:16:22"حول"
00:16:23.
00:16:23يمكنني عرض هذا بشكل ثابت.
00:16:25هذا جيد لبعض التطبيقات، ولكن في حالتي، الصفحة المهمة هي صفحة المنتج، وهذه لا تزال ديناميكية، لذا ليست مفيدة حقاً.
00:16:33ماذا عن هذه الاستراتيجية؟
00:16:35هنا أقوم بإنشاء معلمة سياق الطلب هذه التي تقوم بترميز حالة معينة في عنوان URL الخاص بي، ثم يمكنني استخدام
00:16:42"generate static params"
00:16:44لإنشاء جميع المتغيرات المختلفة لصفحاتي.
00:16:46هذا، بالاشتراك مع جلب بيانات المستخدم من جانب العميل، سيسمح لي بتخزين هذا مؤقتاً في صفحة المنتج الخاصة بي.
00:16:54بالتأكيد نمط قابل للتطبيق.
00:16:55يوصى به بواسطة Vercel Flags SDK ويسمى نمط
00:16:58"precompute"
00:16:59، على ما أعتقد.
00:17:00ولكن هذا معقد حقاً، ولدي طرق متعددة لجلب البيانات.
00:17:04وفي الواقع، لا أريد إعادة كتابة تطبيقي بأكمله بهذا الشكل.
00:17:07ماذا لو لم نضطر إلى القيام بأي من تلك الحلول البديلة؟
00:17:10ماذا لو كان هناك طريقة أبسط؟
00:17:12حسناً، هناك.
00:17:14دعنا نعود إلى تطبيقنا مرة أخرى.
00:17:17لذا يمكننا بالفعل الذهاب إلى التكوين التالي وتمكين مكونات التخزين المؤقت.
00:17:23أوه، رائع.
00:17:25حسناً، وما يفعله هذا، كما تعلمون من الكلمة الرئيسية، هو أنه سيجعل جميع استدعاءاتنا غير المتزامنة تعمل وقت الطلب أو ديناميكياً.
00:17:34وسيعطينا أيضاً أخطاء كلما كان لدينا استدعاء غير متزامن غير معلق، وسيعطينا توجيه
00:17:41"use cache"
00:17:42هذا الذي يمكننا استخدامه لتخزين صفحة أو دالة أو مكون مؤقتاً بشكل دقيق.
00:17:48نعم، دعنا نمضي قدماً ونستفيد من هذا.
00:17:51يمكننا البدء بالصفحة الرئيسية هنا.
00:17:55دعنا نلقي نظرة.
00:17:56لذا مرة أخرى، لدي هذا المزيج من المحتوى الثابت والديناميكي.
00:17:59لدي لافتة الترحيب الخاصة بي، شيء لك أيضاً لي.
00:18:03دعنا نلقي نظرة على ذلك باستخدام مساعد واجهة المستخدم هذا مرة أخرى.
00:18:06لذا على سبيل المثال، يتم عرض اللافتة ديناميكياً بهذا هنا.
00:18:10بينما قمت بوضع علامة على هذا كعرض هجين لأن البطل، يقوم بجلب هذا الشيء غير المتزامن وهو يسير ببطء شديد.
00:18:18لكنه لا يعتمد على أي نوع من بيانات المستخدم أو واجهات برمجة التطبيقات الديناميكية.
00:18:21وهذا يعني أن كل ما يتم عرضه هجينياً هنا يمكن إعادة استخدامه عبر الطلبات وعبر المستخدمين.
00:18:27ويمكننا استخدام توجيه "use cache" على ذلك.
00:18:30لذا دعنا نضيف توجيه
00:18:31"use cache"
00:18:32هنا ونضع علامة على هذا كـ
00:18:34"cached"
00:18:35.
00:18:35وهذا سيسمح لي - كلما أعدت تحميل هذه الصفحة - لم أحفظ هذا.
00:18:43ها نحن ذا.
00:18:44لن يعيد تحميل هذا الجزء لأنه مخزن مؤقتاً.
00:18:47إنه الآن ثابت، أليس كذلك؟
00:18:49وهناك أيضاً واجهات برمجة تطبيقات أخرى ذات صلة مثل
00:18:53"cache tag"
00:18:53للسماح لي بتحديد نوع هذا أو التحقق من صحة إدخال ذاكرة التخزين المؤقت المحدد بدقة أو تحديد فترة الإفصاح الخاصة بي.
00:19:01ولكن لهذا العرض التوضيحي، دعنا نركز فقط على التوجيه البسيط.
00:19:05الآن بعد أن أصبح لدي توجيه
00:19:06"use cache"
00:19:07هذا، يمكنني بالفعل إزالة حدود Suspense الخاصة بي حول هذا البطل.
00:19:10وهذا يعني - حسناً، ما سيفعله هذا هو أن العرض المسبق الجزئي يمكن أن يمضي قدماً ويضمّن هذا في الغلاف المعروض مسبقاً بشكل ثابت بحيث يكون هذا البطل، في هذه الحالة، جزءاً من مخرجات البناء الخاصة بي.
00:19:23دعنا نفعل الشيء نفسه لكل شيء آخر في هذه الصفحة يمكن مشاركته.
00:19:28على سبيل المثال، لدي فئات الميزات هذه هنا.
00:19:31دعنا نمضي قدماً ونفعل الشيء نفسه هناك.
00:19:33ونضيف توجيه "use cache" ونضع علامة على هذا كـ "cached".
00:19:37هكذا.
00:19:39ويمكننا إزالة حدود Suspense.
00:19:40لن نحتاج إلى هذا بعد الآن.
00:19:43الشيء نفسه للمنتجات المميزة.
00:19:44دعنا نضيف "use cache" ونضع علامة على هذا كـ "cached".
00:19:48عفواً.
00:19:50ثم نزيل حدود Suspense.
00:19:52لذا لاحظوا كمية التعقيد التي تمكنت من إزالتها هنا.
00:19:55لا داعي للقلق بشأن هياكلي، أو تحول التخطيط التراكمي الذي كنت أقوم به من قبل.
00:20:00ولم تعد الصفحة - أو لم يعد لدينا هذا القيد على مستوى الصفحة بين الثابت والديناميكي.
00:20:07لذا الآن عندما أحمل هذه الصفحة، سترون أن كل شيء هنا مخزن مؤقتاً باستثناء هذا المحتوى الخاص بالمستخدم حقاً.
00:20:16صحيح.
00:20:18لذا هذا رائع جداً.
00:20:19دعنا نذهب إلى صفحة التصفح ونفعل الشيء نفسه هناك.
00:20:24نعم.
00:20:25لقد قمت بالفعل بوضع علامة على جميع حدودي هنا حتى تتمكنوا من فهم ما يحدث بسهولة.
00:20:29وأريد على الأقل تخزين هذه الفئات مؤقتاً.
00:20:33يبدو أنني أحصل على خطأ، مع ذلك.
00:20:37ربما تتعرفون على هذا.
00:20:38إذن هذا يعني أن لدي مساراً محظوراً.
00:20:40وأنا لا أستخدم حدود Suspense عندما يجب أن أفعل ذلك.
00:20:43تحديث هذا، إنه صحيح، أليس كذلك؟
00:20:46هذا بطيء حقاً.
00:20:47وهو يسبب مشاكل في الأداء وتجربة مستخدم سيئة.
00:20:50لذا هذا رائع.
00:20:51يساعدني
00:20:51"use cache"
00:20:52أو مكونات التخزين المؤقت في تحديد مساراتي المحظورة.
00:20:55دعنا نرى ما يحدث داخل ذلك.
00:20:57إذن هذه هي المشكلة، أليس كذلك؟
00:20:59أنا أجلب هذه الفئات على المستوى الأعلى وليس لدي أي حدود Suspense فوقها.
00:21:03بشكل أساسي، نحتاج إلى اتخاذ قرار.
00:21:05إما أن نضيف حدود Suspense أعلاه أو نختار التخزين المؤقت.
00:21:09دعنا نفعل الشيء البسيط أولاً ونضيف ملف "loading.tsx" هنا.
00:21:12ودعنا نضيف صفحة تحميل هنا، بعض واجهة مستخدم هيكلية لطيفة.
00:21:21هذا جيد جداً.
00:21:21لقد حل الخطأ، ولكن ليس لدي أي شيء مفيد يحدث في هذه الصفحة بينما أنتظر.
00:21:25لا أستطيع حتى البحث.
00:21:27لذا مع مكونات التخزين المؤقت، الديناميكي يشبه - أو الثابت مقابل الديناميكي يشبه الميزان.
00:21:33ويعود إلينا أن نقرر كمية المحتوى الثابت التي نريدها في صفحاتنا.
00:21:37لذا دعنا نحول هذه الصفحة أكثر نحو الثابت ونحذف ملف
00:21:41"loading.tsx"
00:21:42هذا مرة أخرى.
00:21:43ثم نستفيد من الأنماط التي تعلمناها سابقاً لدفع جلب البيانات هذا إلى المكون ووضعه مع واجهة المستخدم.
00:21:50لذا انقل هذا إلى مرشحات الفئات المتجاوبة الخاصة بي هنا.
00:21:54لدي اثنان بسبب التصميم المتجاوب.
00:21:57يمكنني بالفعل المضي قدماً وإضافته هنا.
00:22:01عفواً.
00:22:03واستيراد هذا.
00:22:05لم أعد بحاجة إلى هذا الموجه.
00:22:06في الواقع، مكوني أصبح أكثر قابلية للتركيب.
00:22:09وبدلاً من تعليقه، دعنا نضيف توجيه "use cache".
00:22:14وهذا يجب أن يكون كافياً.
00:22:16لذا لاحظوا كيف أُجبر على التفكير أكثر في مكان حل وعودي، وفي الواقع تحسين بنية مكوناتي من خلال هذا.
00:22:24لا أحتاج إلى تعليق هذا.
00:22:25سيتم تضمين هذا فقط في الغلاف الثابت هنا.
00:22:28قائمة المنتجات، دعني أبقيها محدثة.
00:22:35لذا يمكنني إعادة تحميلها في كل مرة.
00:22:37بينما الفئات في الأسفل، أريد أيضاً تخزينها مؤقتاً.
00:22:41لذا دعنا نمضي قدماً ونذهب إلى التذييل.
00:22:44وبما أنني أستخدم نمط الدونات هنا، يمكن تخزين هذا مؤقتاً حتى لو كان داخل هذا الجزء من واجهة المستخدم التفاعلية.
00:22:54لذا هذا جيد تماماً.
00:22:55لذا لم يكن هذا النمط جيداً للتركيب فحسب، بل أيضاً للتخزين المؤقت.
00:22:58أعتقد أن لدي خطأ آخر هناك.
00:23:03دعنا نرى ما هو ذلك.
00:23:04لا يزال لدي هذا الخطأ.
00:23:08هذا في الواقع بسبب إطارات البحث هذه.
00:23:10إطارات البحث، كما نعلم، هي واجهة برمجة تطبيقات ديناميكية.
00:23:12لا أستطيع تخزين هذا مؤقتاً.
00:23:13ولكن يمكنني حلها أعمق للكشف عن المزيد من واجهة المستخدم الخاصة بي وجعلها ثابتة.
00:23:18لذا دعنا نمضي قدماً وننقل هذا إلى الأسفل، ونمرره كـ
00:23:21"promise"
00:23:22إلى قائمة المنتجات.
00:23:24سنجعل هذا مكتوباً كـ "promise" هنا، هكذا.
00:23:30دعنا نحلها داخل قائمة المنتجات، ونستخدم معلمات البحث المحلولة هنا وهنا.
00:23:36وبما أن هذا معلق هنا، سيختفي الخطأ.
00:23:40لذا عند إعادة تحميل هذا، الشيء الوحيد الذي يتم إعادة تحميله هنا هو الجزء الذي اخترته خصيصاً ليكون ديناميكياً.
00:23:48كل شيء آخر يمكن تخزينه مؤقتاً.
00:23:49وهذا يعني أنه يمكنني التفاعل مع لافتتي أو حتى البحث لأن هذا الجزء قد تم عرضه مسبقاً بالفعل.
00:23:57حسناً، دعنا ننتقل إلى الصفحة الأخيرة هنا، وهي صفحة المنتج، وهي الأصعب والأكثر أهمية.
00:24:05إنها سيئة حقاً الآن.
00:24:08هذا مهم جداً لمنصة التجارة الإلكترونية، على ما يبدو.
00:24:11حسناً، دعنا نمضي قدماً ونصلح هذا أيضاً.
00:24:15لذا هنا لدي صفحة المنتج هذه.
00:24:18دعنا نبدأ بتخزين المحتوى القابل لإعادة الاستخدام هنا مؤقتاً، على سبيل المثال، المنتج نفسه.
00:24:23ونضيف "use cache" هنا ونضع علامة على هذا كـ "cached".
00:24:27هذا يجب أن يكون جيداً.
00:24:28وهذا يعني أنه يمكننا إزالة حدود Suspense هنا.
00:24:33حسناً، وهذا لم يعد يعيد التحميل في كل طلب هنا، أليس كذلك؟
00:24:38بالنسبة لتفاصيل المنتج، دعنا نفعل الشيء نفسه.
00:24:40دعنا نضيف "use cache".
00:24:41دعنا نضع علامة عليه كـ
00:24:43"cached"
00:24:44ونرى إذا كان ذلك سيعمل أيضاً.
00:24:47لم يعمل.
00:24:48في الواقع، هذا خطأ مختلف.
00:24:50إنه يخبرني أنني أحاول استخدام واجهات برمجة تطبيقات ديناميكية داخل هذا الجزء المخزن مؤقتاً.
00:24:54وهذا صحيح.
00:24:54أنا أستخدم زر "حفظ المنتج"، أليس كذلك؟
00:24:56هذا سمح لي بالنقر وتبديل حالة الحفظ.
00:25:00فماذا تعتقدون أننا نستطيع فعله بهذا؟
00:25:03يمكننا استخدام نمط الدونات مرة أخرى.
00:25:06في الواقع، يمكننا أيضاً إدخال أجزاء ديناميكية في أجزاء التخزين المؤقت.
00:25:10لذا نحن ندمجها تماماً كما كان من قبل، ولكن مع التخزين المؤقت.
00:25:12لذا هذا رائع جداً.
00:25:14دعنا نمضي قدماً ونضيف "children" هنا هكذا.
00:25:19وهذا سيزيل الخطأ.
00:25:21ويمكنني ببساطة تغليف هذا حول هذا الجزء الديناميكي الواحد من صفحتي هنا، وإزالة حدود Suspense، وإضافة واجهة مستخدم صغيرة جداً لعلامة مرجعية لهذا الجزء الديناميكي الواحد من الصفحة.
00:25:34ودعنا نرى كيف يبدو ذلك الآن.
00:25:40لذا لاحظوا كيف أن واجهة المستخدم بأكملها تقريباً متاحة، ولكن لدي هذا الجزء الصغير الواحد الذي هو ديناميكي، وهذا جيد.
00:25:47كل شيء آخر لا يزال موجوداً.
00:25:48ودعنا نترك المراجعات ديناميكية لأنه يمكننا إبقائها محدثة.
00:25:53لا يزال هناك خطأ آخر.
00:25:54دعنا نعالج ذلك بسرعة.
00:25:56مرة أخرى، هذه هي الـ "params".
00:25:58أحصل على مساعدة بأنني بحاجة إلى اتخاذ قرار إما بإضافة بديل تحميل أو تخزين هذا مؤقتاً.
00:26:04دعنا نستخدم "generate static params" في هذه الحالة.
00:26:07يعتمد نوعاً ما على حالة استخدامك ومجموعة بياناتك.
00:26:10ولكن في هذه الحالة، سأضيف فقط بضع صفحات محددة مسبقاً ومعروضة مسبقاً، ثم أقوم بتخزين الباقي مؤقتاً أثناء إنشائها بواسطة المستخدمين.
00:26:17وهذا سيزيل خطأي هنا.
00:26:20لذا أعتقد أنني انتهيت بالفعل من إعادة الهيكلة الخاصة بي.
00:26:22دعنا نمضي قدماً ونلقي نظرة على الإصدار المنشور ونرى كيف يبدو ذلك.
00:26:26لذا قمت بنشر هذا للتو على Vercel.
00:26:27وتذكروا، لقد أبطأت عمداً الكثير من عمليات جلب البيانات هنا.
00:26:35ومع ذلك، عندما أحمل هذه الصفحة في البداية، كل شيء متاح بالفعل.
00:26:40الشيء الوحيد هنا هو تلك الأجزاء الديناميكية القليلة مثل الخصومات و
00:26:46"لك"
00:26:46.
00:26:46الشيء نفسه مع "تصفح الكل".
00:26:47واجهة المستخدم بأكملها متاحة بالفعل.
00:26:50وبالنسبة للمنتج نفسه، فإنه يبدو فورياً.
00:26:54وتذكروا، مرة أخرى، أن جميع هذه الأجزاء المخزنة مؤقتاً ستُضمّن مع الغلاف الثابت مع العرض المسبق الجزئي.
00:27:00ويمكن جلبها مسبقاً باستخدام الجلب المسبق المحسّن في موجه العميل الجديد Next 16.
00:27:05وهذا يعني أن كل عملية تنقل - إنها تبدو سريعة جداً، أليس كذلك؟
00:27:09حسناً، للتلخيص، مع مكونات التخزين المؤقت، لم يعد هناك ثابت مقابل ديناميكي.
00:27:17ولم نعد بحاجة إلى تجنب واجهات برمجة التطبيقات الديناميكية أو المساومة على المحتوى الديناميكي.
00:27:28ويمكننا تجاوز هذه الاختراقات المعقدة والحلول البديلة باستخدام استراتيجيات جلب بيانات متعددة فقط من أجل ذلك - هذا التخزين المؤقت الناجح، كما أريتكم.
00:27:37لذا في Next.js الحديثة، الديناميكي مقابل الثابت هو مقياس.
00:27:40ونحن نقرر كمية المحتوى الثابت التي نريدها في تطبيقاتنا.
00:27:43وطالما اتبعنا أنماطاً معينة، يمكننا أن يكون لدينا نموذج ذهني واحد، وهو فعال وقابل للتركيب وقابل للتوسع بشكل افتراضي.
00:27:50لذا دعنا نعود إلى الشرائح.
00:27:53لذا إذا لم تكونوا - لم نكن بالفعل معجبين بسرعة ذلك، فهذه هي نتيجة Lighthouse.
00:27:56لذا جمعت بعض البيانات الميدانية باستخدام Vercel Speed Insights.
00:28:00لذا لدينا درجة 100 في جميع الصفحات الأكثر أهمية، الصفحة الرئيسية، صفحة المنتج، وقائمة المنتجات، على الرغم من أنها ديناميكية للغاية.
00:28:08لذا دعنا نلخص أخيراً الأنماط التي ستضمن قابلية التوسع والأداء في تطبيقات Next.js وتسمح لنا بالاستفادة من أحدث الابتكارات والحصول على نتائج كهذه.
00:28:18أولاً، يمكننا تحسين بنيتنا عن طريق حل الوعود عميقاً في شجرة المكونات وجلب البيانات محلياً داخل المكونات باستخدام React Cache لتجنب تكرار العمل.
00:28:28يمكننا تجنب تمرير الخصائص الزائد إلى مكونات العميل باستخدام موفري السياق (context providers) بالاشتراك مع React Use.
00:28:35ثانياً، يمكننا تركيب مكونات العميل والخادم باستخدام نمط الدونات لتقليل جافاسكريبت من جانب العميل، والحفاظ على فصل واضح للمسؤوليات، والسماح بإعادة استخدام المكونات.
00:28:43وسيمكننا هذا النمط أيضاً من تخزين مكونات الخادم المركبة مؤقتاً لاحقاً.
00:28:50وأخيراً، يمكننا التخزين المؤقت والعرض المسبق باستخدام
00:28:52"use cache"
00:28:53إما حسب الصفحة أو المكون أو الدالة للقضاء على المعالجة الزائدة، وتعزيز الأداء وتحسين محركات البحث (SEO)، والسماح للعرض المسبق الجزئي بعرض هذه الأجزاء من التطبيق بشكل ثابت.
00:29:01وإذا كان محتوانا ديناميكياً حقاً، يمكننا تعليقه ببدائل تحميل مناسبة.
00:29:07وتذكروا أن كل هذا مترابط.
00:29:09لذا كلما كانت بنيتك أفضل، كلما كان التركيب أسهل، وكلما كان التخزين المؤقت والعرض المسبق أسهل بأفضل النتائج.
00:29:15على سبيل المثال، حل واجهات برمجة التطبيقات الديناميكية عميقاً في الشجرة سيسمح لك بإنشاء غلاف ثابت أكبر معروض جزئياً.
00:29:22وبهذا، هذا هو مستودع النسخة المكتملة من التطبيق.
00:29:25هناك الكثير من الأشياء التي لم أظهرها حتى هناك ويمكنكم التحقق منها.
00:29:29ويمكنكم مسح رمز الاستجابة السريعة للعثور على حساباتي الاجتماعية هناك بجانب المستودع إذا كنتم لا ترغبون في التقاط صورة وكتابتها بأنفسكم.
00:29:36نعم، هذا كل شيء بالنسبة لي.
00:29:37شكراً لمؤتمر Next.js على استضافتي هنا.
00:29:39[موسيقى تعزف]

Key Takeaway

تتيح Next.js 16، من خلال توجيه use cache وأنماط التركيب مثل نمط الدونات، للمطورين بناء تطبيقات ويب عالية الأداء وقابلة للتوسع عن طريق دمج المحتوى الثابت والديناميكي بفعالية وتقليل جافاسكريبت من جانب العميل.

Highlights

تقديم توجيه use cache في Next.js 16 لتمكين التخزين المؤقت القابل للتركيب والعرض المسبق الجزئي.

حل مشكلة تمرير الخصائص (Prop Drilling) باستخدام مكونات الخادم و React Cache وموفري السياق.

تخفيض جافاسكريبت الزائدة من جانب العميل والمكونات الكبيرة باستخدام نمط الدونات لفصل الاهتمامات.

تحسين أداء التطبيقات وتكاليف الخادم من خلال تطبيق استراتيجيات التخزين المؤقت الدقيقة على مستوى المكونات.

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

تحقيق درجات Lighthouse مثالية (100) للصفحات الرئيسية بعد تطبيق هذه الأنماط.

التأكيد على أن العلاقة بين العرض الثابت والديناميكي في Next.js الحديثة هي مقياس يحدده المطور.

Timeline

المقدمة وأهداف الحديث

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

أساسيات العرض الثابت والديناميكي

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

محفزات العرض الديناميكي

توضح المتحدثة واجهات برمجة التطبيقات المحددة التي يمكن أن تتسبب في عرض الصفحة ديناميكياً. تشمل هذه الواجهات استخدام خصائص `params` و `search params`، على الرغم من إمكانية استخدام `static params` للعرض المسبق. كما أن قراءة ملفات تعريف الارتباط ورؤوس الطلبات الواردة ستؤدي إلى العرض الديناميكي، مع ملاحظة أن محاولة التخزين المؤقت باستخدام هذه المعلومات ستسبب أخطاء. أخيراً، استخدام `fetch` مع تكوين ذاكرة التخزين المؤقت 'no store' سيفرض أيضاً العرض الديناميكي. هذه المعرفة حاسمة لتجنب العرض الديناميكي غير المقصود وتحسين أداء التطبيق.

تحديات النهج القديم في Next.js

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

تقديم توجيه use cache في Next.js 16

تقدم المتحدثة الحل لمشكلة العرض الثابت/الديناميكي الشامل: توجيه `use cache`، الذي تم الإعلان عنه في مؤتمر Next.js الماضي وأصبح متاحاً في Next.js 16. مع `use cache`، لم تعد الصفحات تُجبر على العرض الثابت أو الديناميكي، بل يمكن أن تكون كلاهما. يصبح كل شيء ديناميكياً افتراضياً، ويسمح `use cache` بالاشتراك صراحةً في التخزين المؤقت على مستوى المكون أو الدالة. هذا يتيح التخزين المؤقت القابل للتركيب والعرض المسبق الجزئي، مما يعني أن الأجزاء المخزنة مؤقتاً يمكن تضمينها كجزء من الغلاف الثابت، مما يحسن وقت تحميل الصفحة.

إعداد العرض التوضيحي والمشكلات الشائعة

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

حل مشكلة تمرير الخصائص (Prop Drilling)

تتناول المتحدثة المشكلة الأولى: تمرير الخصائص المتعمق، حيث يتم تمرير خاصية 'logged in' عبر مستويات متعددة. أفضل ممارسة هي دفع جلب البيانات إلى المكونات التي تستخدمها وحل الوعود أعمق في الشجرة باستخدام React Cache. بالنسبة لمكونات العميل، يتم استخدام نمط ذكي يتضمن موفر مصادقة (auth provider) في التخطيط الجذري لتمرير وعد المصادقة، مما يسمح للمكونات الفرعية بقراءته محلياً باستخدام `use auth` و `use`. هذا النمط يحافظ على بنية مكونات جيدة، ويجعل المكونات أكثر قابلية للاستخدام والتركيب، ويزيل التبعيات الغريبة من الصفحة الرئيسية.

نمط الدونات لتقليل جافاسكريبت من جانب العميل

تنتقل أورورا إلى معالجة جافاسكريبت الزائدة من جانب العميل والمكونات الكبيرة باستخدام 'نمط الدونات'. يتم استخراج منطق واجهة المستخدم التفاعلي (مثل حالة 'dismissed' في لافتة الترحيب) إلى غلاف من جانب العميل (`BannerContainer`) يحيط بمحتوى معروض من الخادم. هذا يسمح للمكون الأساسي بأن يكون مكون خادم يجلب البيانات، مما يقلل من جافاسكريبت من جانب العميل ويحسن أمان النوع. يتم تطبيق النمط أيضاً على مكتبات الرسوم المتحركة (Motion) ومكون 'showMore' في التذييل، مما يجعل المكونات قابلة للتركيب وإعادة الاستخدام.

مشكلة نقص استراتيجيات العرض الثابت والحلول القديمة

توضح أورورا أن جميع صفحات التطبيق ديناميكية بسبب استخدام ملفات تعريف الارتباط في التخطيط الجذري لجلب بيانات المستخدم، مما يؤدي إلى تكاليف خادم إضافية وتدهور في الأداء. تناقش الحلول البديلة السابقة لهذه المشكلة، مثل إنشاء مجموعات مسارات لتقسيم التطبيق إلى أقسام ثابتة وديناميكية، أو استخدام نمط 'precompute' مع `generate static params` وجلب بيانات المستخدم من جانب العميل. تشير إلى أن هذه الحلول كانت معقدة وتتطلب إعادة كتابة كبيرة للتطبيق. هذا القسم يبرز الحاجة إلى حل أبسط وأكثر فعالية.

تطبيق use cache للعرض المسبق الجزئي (الصفحة الرئيسية)

تقدم أورورا الحل الأبسط: تمكين 'cache components' في تكوين Next.js. ثم توضح كيفية تطبيق توجيه `use cache` على مكونات محددة على الصفحة الرئيسية (مثل Hero، الفئات المميزة، المنتجات المميزة) التي لا تعتمد على بيانات المستخدم الديناميكية. هذا يسمح بتخزين هذه الأجزاء مؤقتاً وضمها في الغلاف الثابت المعروض مسبقاً، مما يلغي الحاجة إلى حدود Suspense حولها. هذا النهج يقلل من التعقيد ويحسن وقت التحميل الأولي للصفحة بشكل كبير، محولاً الصفحة من ديناميكية بالكامل إلى مزيج فعال من الثابت والديناميكي.

تطبيق use cache للعرض المسبق الجزئي (صفحة التصفح)

تواصل أورورا تطبيق `use cache` على صفحة التصفح، حيث تحاول تخزين الفئات مؤقتاً. تواجه خطأ 'مسار محظور' بسبب جلب البيانات على المستوى الأعلى بدون Suspense، مما يؤثر سلباً على الأداء. تحل المشكلة عن طريق دفع جلب البيانات إلى مكون `ResponsiveCategoryFilters` وتطبيق `use cache` عليه، مما يجبرها على التفكير في بنية المكونات. كما تعالج `search params` الديناميكية عن طريق تمريرها كـ 'promise' إلى `ProductList` وحلها هناك، مما يضمن أن الأجزاء الديناميكية فقط هي التي يتم إعادة تحميلها، بينما تظل الأجزاء الثابتة متاحة فوراً.

تطبيق use cache للعرض المسبق الجزئي (صفحة المنتج)

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

نتائج العرض التوضيحي وملخص use cache

تعرض أورورا النتائج النهائية للتطبيق المنشور على Vercel، حيث تظهر الصفحات فورية التحميل على الرغم من التأخيرات المتعمدة في جلب البيانات. تؤكد أن `use cache` يحول العلاقة بين العرض الثابت والديناميكي إلى مقياس، مما يسمح للمطورين بتحديد كمية المحتوى الثابت في تطبيقاتهم. تشير إلى أن الأجزاء المخزنة مؤقتاً تُضمّن مع الغلاف الثابت من خلال العرض المسبق الجزئي، ويمكن جلبها مسبقاً باستخدام الجلب المسبق المحسّن في Next.js 16. هذا يؤدي إلى تنقلات سريعة جداً وتجربة مستخدم محسنة.

الخلاصة والأنماط الرئيسية لـ Next.js الحديثة

تلخص أورورا الأنماط الرئيسية التي تضمن قابلية التوسع والأداء في تطبيقات Next.js الحديثة، مستعرضة درجات Lighthouse المثالية (100) للصفحات الرئيسية. تشمل هذه الأنماط: تحسين البنية عن طريق جلب البيانات محلياً باستخدام React Cache وتجنب تمرير الخصائص بموفري السياق و React Use. ثانياً، تركيب مكونات العميل والخادم باستخدام نمط الدونات لتقليل جافاسكريبت من جانب العميل. وأخيراً، التخزين المؤقت والعرض المسبق باستخدام `use cache` على مستوى الصفحة أو المكون أو الدالة. تؤكد على أن هذه الأنماط مترابطة، حيث تؤدي البنية الأفضل إلى تركيب وتخزين مؤقت أسهل ونتائج أفضل، وتوفر رابطاً لمستودع النسخة المكتملة من التطبيق.

Community Posts

View all posts