تقسيم الرمز باستخدام React.lazy وSuspense

لست بحاجة إلى شحن رموز أكثر من اللازم إلى المستخدمين مطلقًا، لذا قسِّم الحزم لضمان عدم حدوث ذلك مطلقًا.

تسهّل الطريقة React.lazy تقسيم رمز تطبيق React على مستوى المكوّنات باستخدام عمليات الاستيراد الديناميكية.

import React, { lazy } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const DetailsComponent = () => (
  <div>
    <AvatarComponent />
  </div>
)

لماذا يُعدّ هذا الردّ مفيدًا؟

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

توفّر الدالة React.lazy طريقة مدمَجة لفصل المكوّنات في أحد التطبيقات إلى أجزاء منفصلة من JavaScript مع بذل مجهود بسيط. يمكنك عندئذٍ العناية بحالات التحميل عند إقرانها بالمكوِّن Suspense.

Suspense

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

ومع ذلك، سيكون هناك دائمًا تأخير بسيط على المستخدمين عند جلب أحد المكوّنات المقسّمة حسب الرمز عبر الشبكة، لذا من المهم عرض حالة تحميل مفيدة. يساعد استخدام React.lazy مع المكوّن Suspense في حل هذه المشكلة.

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
  </Suspense>
)

تقبل السمة Suspense المكوِّن fallback الذي يتيح لك عرض أي مكوّن React كحالة تحميل. يوضح المثال التالي كيفية عمل ذلك. لا يتم عرض الصورة الرمزية إلا عند النقر على الزر، حيث يتم تقديم طلب لاسترداد الرمز اللازم لتعليق AvatarComponent المعلّق. وفي الوقت الحالي، يتم عرض مكوّن التحميل الاحتياطي.

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

لتوضيح كيفية عمل ذلك بشكل أفضل:

  • لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق، ثم اضغط على ملء الشاشة ملء الشاشة.
  • اضغط على "Control+Shift+J" (أو "Command+Option+J" على نظام التشغيل Mac) لفتح "أدوات مطوّري البرامج".
  • انقر على علامة التبويب الشبكة.
  • انقر على القائمة المنسدلة التقييد، والتي يتم ضبطها على بدون تقييد تلقائيًا. اختَر الجيل الثالث السريع.
  • انقر على زر انقر فوقي في التطبيق.

سيظهر مؤشر التحميل لمدة أطول الآن. لاحظ كيف يتم جلب كل التعليمات البرمجية التي يتألف منها AvatarComponent كمقطع منفصل.

لوحة الشبكة الخاصة بأدوات مطوّري البرامج تعرض ملف shortcuts.js واحد يتم تنزيله

تعليق مكونات متعددة

من الميزات الأخرى في Suspense أنّه يسمح لك بتعليق تحميل عدّة مكوّنات، حتى إذا تم تحميلها كلها باستخدام "التحميل الكسول".

مثال:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
    <InfoComponent />
    <MoreInfoComponent />
  </Suspense>
)

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

يمكنك مشاهدة هذا من خلال التضمين التالي:

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

التعامل مع حالات تعذُّر التحميل

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

يستخدم التفاعل نمطًا عاديًا للتعامل بشكل سلس مع الأنواع التالية من حالات تعذُّر التحميل، وهي: استخدام حدود الخطأ. كما هو موضّح في المستندات، يمكن أن يعمل أي مكوّن React كحدود للخطأ في حال تنفيذ إما (أو كليهما) طريقتَي دورة الحياة static getDerivedStateFromError() أو componentDidCatch().

لرصد حالات تعذُّر التحميل الكسول والتعامل معها، يمكنك تضمين مكوّنات Suspense ضمن مكوّنات رئيسية كحدّ للخطأ. داخل طريقة render() لحدود الخطأ، يمكنك عرض العناصر الفرعية كما هي في حال عدم وجود خطأ أو عرض رسالة خطأ مخصّصة إذا حدث خطأ ما:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false};
  }

  static getDerivedStateFromError(error) {
    return {hasError: true};
  }

  render() {
    if (this.state.hasError) {
      return <p>Loading failed! Please reload.</p>;
    }

    return this.props.children;
  }
}

const DetailsComponent = () => (
  <ErrorBoundary>
    <Suspense fallback={renderLoader()}>
      <AvatarComponent />
      <InfoComponent />
      <MoreInfoComponent />
    </Suspense>
  </ErrorBoundary>
)

الخلاصة

إذا لم تكن متأكدًا من أين تبدأ تطبيق تقسيم الرمز على تطبيق React، اتّبِع الخطوات التالية:

  1. ابدأ على مستوى المسار. تعد المسارات أبسط طريقة لتحديد النقاط التي يمكن تقسيمها في تطبيقك. تعرض مستندات التفاعل كيفية استخدام Suspense مع react-router.
  2. حدِّد أي مكوّنات كبيرة على صفحة موقعك الإلكتروني لا يتم عرضها إلا على تفاعلات معيّنة للمستخدمين (مثل النقر على زر). يؤدي تقسيم هذه المكونات إلى تقليل حمولات JavaScript.
  3. ضع في اعتبارك تقسيم أي شيء آخر خارج الشاشة وليس بالغ الأهمية للمستخدم.