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

سيظهر مؤشر التحميل لفترة أطول الآن. لاحظ كيف يتم جلب كل الرمز الذي يشكّل 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>
)

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

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

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

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

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

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