تقسيم الرمز باستخدام 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 كبيرة للمستخدمين في طول الوقت الذي يستغرقه تحميل الصفحة، خاصةً على الأجهزة واتصالات الشبكة الأضعف. لهذا السبب، فإنّ تقسيم الرموز البرمجية وميزة "التحميل البطيء" مفيدة للغاية.

مع ذلك، سيكون هناك دائمًا تأخير بسيط يجب أن يواجهه المستخدمون عند جلب مكوّن تقسيم الرمز عبر الشبكة، لذا من المهم عرض حالة تحميل مفيدة. ويساعد استخدام 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) لفتح "أدوات مطوري البرامج".
  • انقر على علامة التبويب الشبكة.
  • انقر على القائمة المنسدلة تقييد السرعة، والتي تكون مضبوطة على بدون تقييد السرعة تلقائيًا. اختَر شبكة الجيل الثالث السريعة.
  • انقر على الزر انقر على Me في التطبيق.

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

لوحة شبكة في &quot;أدوات مطوّري البرامج&quot; تعرض ملف sample.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. ننصحك بتقسيم أي محتوى آخر خارج الشاشة وغير مهم للمستخدم.