تقسيم الرمز باستخدام عمليات الاستيراد الديناميكية في Next.js

كيفية تسريع تطبيق Next.js باستخدام ميزة "تقسيم الرموز البرمجية" واستراتيجيات التحميل الذكية

ما الذي ستتعلمه؟

تشرح هذه المشاركة الأنواع المختلفة من تقسيم الرموز وكيفية استخدام عمليات الاستيراد الديناميكية لتسريع تطبيقات Next.js.

تقسيم الرموز البرمجية استنادًا إلى المسار والمكوّنات

يقسّم Next.js تلقائيًا JavaScript إلى أجزاء منفصلة لكل مسار. عندما يحمِّل المستخدمون تطبيقك، لا ترسل Next.js سوى الرمز البرمجي المطلوب للمسار الأولي. عندما ينتقل المستخدمون في التطبيق، يجلبون الأجزاء المرتبطة بالمسارات الأخرى. يعمل تقسيم الرموز البرمجية المستند إلى المسار على تقليل مقدار النص البرمجي الذي يجب تحليله وتجميعه في آنٍ واحد، ما يؤدي إلى تقليل أوقات تحميل الصفحة.

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

تتوافق Next.js مع import() الديناميكي، ما يتيح لك استيراد وحدات JavaScript (بما في ذلك مكونات React) بشكل ديناميكي وتحميل كل عملية استيراد كجزء منفصل. ويمنحك ذلك تقسيم الرموز على مستوى المكوّنات ويتيح لك التحكّم في تحميل الموارد، حتى لا ينزّل المستخدمون الرمز إلا الذي يحتاجون إليه لجزء من الموقع الإلكتروني الذي يشاهدونه. في Next.js، يتم عرض هذه المكوّنات من جهة الخادم (SSR) تلقائيًا.

عمليات الاستيراد الديناميكية في العمل

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

في الإصدار الأول من التطبيق، يعيش الجرو في components/Puppy.js. لعرض شكل الجرو على الصفحة، يستورد التطبيق المكوِّن Puppy باللغة index.js مع عبارة استيراد ثابتة:

import Puppy from "../components/Puppy";

لمعرفة كيفية تجميع Next.js للتطبيق، عليك فحص تتبُّع الشبكة في DevTools:

  1. لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق، ثم اضغط على ملء الشاشة ملء الشاشة.

  2. اضغط على Ctrl ‏+ Shift ‏+ J (أو Command ‏+ Option ‏+ J على نظام التشغيل Mac) لفتح DevTools.

  3. انقر على علامة التبويب الشبكة.

  4. ضَع علامة في مربّع الاختيار إيقاف ذاكرة التخزين المؤقت.

  5. إعادة تحميل الصفحة

عند تحميل الصفحة، يتم تجميع كل الرموز البرمجية اللازمة، بما في ذلك Puppy.js المكوّن، في index.js:

علامة تبويب "شبكة أدوات مطوّري البرامج" تعرض ستة ملفات JavaScript وهي: index.js وapp.js وwebpack.js وmain.js و0.js وملف dll (مكتبة الروابط الديناميكية).

عند الضغط على الزر Click me (انقر عليّ)، تتم إضافة طلب صورة الجرو بتنسيق JPEG فقط إلى علامة التبويب الشبكة:

علامة التبويب "الشبكة" في DevTools بعد النقر على الزرّ، تعرِض الملفات الستة نفسها من JavaScript وصورة واحدة.

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

تحقق الآن من إصدار ثاني من التطبيق، يتم فيه استبدال الاستيراد الثابت بالاستيراد الديناميكي. يتضمّن Next.js next/dynamic، ما يجعله ممكنًا استخدام عمليات الاستيراد الديناميكية لأي مكوّنات في Next:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

اتّبِع الخطوات الواردة في المثال الأول لفحص عملية تتبُّع الشبكة.

عند تحميل التطبيق لأول مرة، يتم تنزيل index.js فقط. هذه المرة، أصبح حجمه أصغر بمقدار 0.5 كيلوبايت (انخفض من 37.9 كيلوبايت إلى 37.4 كيلوبايت) لأنّه لا يتضمّن رمز المكوّن Puppy:

شبكة أدوات مطوّري البرامج تعرض ملفات JavaScript الستة نفسها باستثناء حجم index.js الآن بمقدار 0.5 كيلوبايت

أصبح المكوّن Puppy الآن في قطعة منفصلة، 1.js، لا يتم تحميلها إلا عند الضغط على الزر:

علامة التبويب "الشبكة" في "أدوات المطوّر" بعد النقر على الزر، تعرِض الملف الإضافي 1.js والصورة المُضافة إلى أسفل قائمة الملفات.

في التطبيقات الواقعية، تكون المكوّنات غالبًا أكبر كثيرًا، ويمكن أن يؤدي تحميلها بشكلٍ بطيء إلى تقليل حجم الحمولة الأولية لملف JavaScript بمئات الكيلوبايت.

عمليات الاستيراد الديناميكية مع مؤشر تحميل مخصّص

عند التحميل الكسول للموارد، من الأفضل توفير مؤشر تحميل في حال حدوث أي تأخير. في Next.js، يمكنك إجراء ذلك من خلال تقديم وسيطة إضافية للدالة dynamic():

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

للاطّلاع على مؤشر التحميل أثناء العمل، يمكنك محاكاة الاتصال البطيء بالشبكة في DevTools:

  1. لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق. ثم اضغط على ملء الشاشة ملء الشاشة.

  2. اضغط على Ctrl ‏+ Shift ‏+ J (أو Command ‏+ Option ‏+ J على نظام التشغيل Mac) لفتح DevTools.

  3. انقر على علامة التبويب الشبكة.

  4. ضَع علامة في مربّع الاختيار إيقاف ذاكرة التخزين المؤقت.

  5. في القائمة المنسدلة تقييد السرعة، اختَر شبكة الجيل الثالث السريعة.

  6. اضغط على الزر انقر هنا.

الآن عند النقر على الزر، يستغرق تحميل المكوّن بعض الوقت ويعرض التطبيق الرسالة "جارٍ التحميل" في هذه الأثناء.

شاشة داكنة تعرض النص

عمليات الاستيراد الديناميكية بدون إعادة التحميل من جهة العميل

إذا كنت بحاجة إلى عرض مكوّن على جانب العميل فقط (مثل ملف شخصي للدردشة)، يمكنك إجراء ذلك من خلال ضبط الخيار ssr على false:

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

الخاتمة

من خلال إتاحة عمليات الاستيراد الديناميكية، يوفّر لك Next.js ميزة تقسيم الرموز على مستوى المكوّنات، ما يمكن أن يؤدي إلى تقليل أحمال JavaScript وتحسين وقت تحميل التطبيق. يتم عرض جميع المكوّنات من جهة الخادم تلقائيًا ويمكنك إيقاف هذا الخيار متى لزم الأمر.