تقسيم الرمز باستخدام 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.

تشويق

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

ومع ذلك، سيواجه المستخدمون دائمًا تأخيرًا طفيفًا عند retrieving a code-split component over the network، لذا من المهم عرض حالة تحميل مفيدة. يساعد استخدام 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 صغير، لذلك لا يظهر مؤشر التحميل إلا لفترة قصيرة. يمكن أن يستغرق تحميل المكوّنات الأكبر حجمًا وقتًا أطول بكثير، خاصةً عند استخدام اتصالات شبكة ضعيفة.

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

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

سيظهر مؤشر التحميل لفترة أطول الآن. لاحظ كيف يتم جلب كل الرمز الذي يشكّل AvatarComponent كجزء منفصل.

لوحة الشبكة في &quot;أدوات مطوّري البرامج&quot; تعرِض ملفًا واحدًا من ملفّات chunk.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>
)

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

يمكنك الاطّلاع على ذلك من خلال عملية التضمين التالية:

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

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

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

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

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

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