3 طرق لتقديم قوائم كبيرة في الزاوي

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

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







على الرغم من أن هذه المادة موجهة إلى Angular ، فإن ما تتم مناقشته هنا ينطبق على أطر العمل والمشاريع الأخرى المكتوبة بلغة JavaScript خالصة. على وجه الخصوص ، سيتم النظر في الأساليب التالية لتقديم قوائم كبيرة هنا:

  • التمرير الظاهري (باستخدام Angular CDK).
  • التقديم اليدوي.
  • التقديم التدريجي.

التمرير الافتراضي


ربما يكون التمرير الافتراضي هو الطريقة الأكثر فعالية للعمل مع القوائم الكبيرة. صحيح ، لديه بعض العيوب. من السهل جدًا تنفيذ التمرير الظاهري ، بفضل Angular CDK والمكونات الإضافية الأخرى ، في أي مكون.

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

سنستخدم الوحدة النمطية scrollingمن Angular CDK ، المصممة لتنظيم التمرير الافتراضي. للقيام بذلك ، تحتاج أولاً إلى تثبيت CDK:

npm i @angular/cdk

ثم تحتاج إلى استيراد الوحدة:

import { ScrollingModule } from '@angular/cdk/scrolling';
@NgModule({
 ...
 imports: [ ScrollingModule, ...]
})
export class AppModule {}

بعد ذلك ، في المكونات يمكنك استخدام cdk-virtual-scroll-viewport:

<cdk-virtual-scroll-viewport itemSize="50">
 <div *cdkVirtualFor="let item of items">
   {{ item }}
 </div>
</cdk-virtual-scroll-viewport>

فيما يلي مثال لمشروع يستخدم هذا الأسلوب لتنظيم التمرير الظاهري.

كما ترون ، فإن الآليات الزاوية القياسية تسمح ، دون صعوبة كبيرة ، بتحقيق نتائج رائعة. يجعل المكون في المثال عدة آلاف من العناصر دون أي مشاكل.

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

  • , , . , . , Autocomplete ( ). , , , , . — .
  • — , .
  • هناك بعض المشكلات المتعلقة بإمكانية الوصول وسهولة استخدام محتويات القائمة مع التمرير الظاهري. لا يتم عرض العناصر المخفية - وهذا يعني أنها لن تكون متاحة لقارئي الشاشة ، وأنه لا يمكن العثور عليها في الصفحة باستخدام آليات المتصفح القياسية.

يعد التمرير الافتراضي مثاليًا في عدد من المواقف (شريطة أن يعمل):

  • في هذه الحالة ، إذا كنت ترغب في عرض القوائم التي لا يُعرف حجمها مسبقًا ، أو تلك التي يمكن أن تكون ضخمة (وفقًا لتقدير تقريبي ، القوائم التي تحتوي على أكثر من 5 آلاف عنصر ، ولكن هذا يعتمد بشكل كبير على مدى تعقيد كل عنصر).
  • في حال كنت بحاجة إلى تنظيم التمرير اللانهائي.

التقديم اليدوي


إحدى طرق العمل مع القوائم التي حاولت تسريع إخراج مجموعة كبيرة من العناصر هي استخدام العرض اليدوي باستخدام Angular API بدلاً من ذلك *ngFor.

لدينا قالب بسيط يستخدم حلقة منظمة باستخدام التوجيه *ngFor

<tr 
*ngFor="let item of data; trackBy: trackById; let isEven = even; let isOdd = odd"
    class="h-12"
    [class.bg-gray-400]="isEven"
    [class.bg-gray-500]="isOdd"
>
  <td>
    <span class="py-2 px-4">{{ item.id }}</span>
  </td>

  <td>
    <span>{{ item.label }}</span>
  </td>

  <td>
    <a>
      <button class="py-2 px-4 rounded (click)="remove(item)">x</button>
    </a>
  </td>
</tr>

لقياس أداء العرض لـ 10000 عنصر بسيط ، استخدمت مقياسًا مرجعيًا يعتمد على مقياس js-framework -معيار .

أولاً ، قمت بفحص أداء القائمة التي تستخدم حلقة منتظمة للإخراج *ngFor. ونتيجة لذلك ، اتضح أن تنفيذ الكود (البرمجة النصية) استغرق 1099 مللي ثانية ، والعرض (التقديم) استغرق 1553 مللي ثانية ، والرسم (الرسم) - 3 مللي ثانية.


دراسة لأداء القائمة التي تستخدم * ngFor لعرض المخرجات.

يمكن عرض عناصر القائمة يدويًا باستخدام واجهة برمجة تطبيقات Angular:

<tbody>
  <ng-container #itemsContainer></ng-container>
</tbody>
<ng-template #item let-item="item" let-isEven="isEven">
  <tr class="h-12 "
      [class.bg-gray-400]="isEven"
      [class.bg-gray-500]="!isEven"
  >
    <td>
      <span class="py-2 px-4">{{ item.id }}</span>
    </td>

    <td>
      <span>{{ item.label }}</span>
    </td>

    <td>
      <a>
        <button class="py-2 px-4 rounded" (click)="remove(item)">x</button>
      </a>
    </td>
  </tr>
</ng-template>

لنتحدث عن كيفية تغير رمز وحدة التحكم.

أعلنا عن نموذج وحاوية:

@ViewChild('itemsContainer', { read: ViewContainerRef }) container: ViewContainerRef;
@ViewChild('item', { read: TemplateRef }) template: TemplateRef<any>;

عند إنشاء البيانات ، نقدمها باستخدام طريقة createEmbeddedViewالكيان ViewContainerRef:

private buildData(length: number) {
  const start = this.data.length;
  const end = start + length;

  for (let n = start; n <= end; n++) {
    this.container.createEmbeddedView(this.template, {
      item: {
        id: n,
        label: Math.random()
      },
      isEven: n % 2 === 0
    });
  }
}

ونتيجة لذلك ، تحسنت المؤشرات التي تميز أداء القائمة قليلاً. أي ، استغرق الأمر 734 مللي ثانية لتنفيذ الرمز ، و 1443 مللي ثانية للعرض ، و 2 مللي ثانية للرسم.


عند البحث عن أداء قائمة تستخدم واجهة برمجة تطبيقات Angular لإخراج

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

إليك ما يبدو عليه (هنا أقوم باستخدام الماوس بتقليد مؤشر التحميل).


List Slowing

Now ، لتحسين عرض القائمة يدويًا ، سنحاول تطبيق تقنية العرض التدريجي.

التقديم التدريجي


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

الكود أدناه الذي ينفذ تقديم القائمة التقدمية واضح جدًا:

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

private buildData(length: number) {
  const ITEMS_RENDERED_AT_ONCE = 500;
  const INTERVAL_IN_MS = 10;

  let currentIndex = 0;

  const interval = setInterval(() => {
    const nextIndex = currentIndex + ITEMS_RENDERED_AT_ONCE;

    for (let n = currentIndex; n <= nextIndex ; n++) {
      if (n >= length) {
        clearInterval(interval);
        break;
      }
      const context = {
        item: {
          id: n,
          label: Math.random()
        },
        isEven: n % 2 === 0
      };
      this.container.createEmbeddedView(this.template, context);
    }

    currentIndex += ITEMS_RENDERED_AT_ONCE;
  }, INTERVAL_IN_MS);

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

من خلال قياس أداء هذا الحل ، حصلت على نتائج تبدو أسوأ من تلك التي تلقيتها سابقًا. تنفيذ الرمز - 907 مللي ثانية ، العرض - 2555 مللي ثانية ، الرسم - 16 مللي ثانية.


البحث عن أداء القائمة ، التي تستخدم نتيجتها العرض التدريجي ،

لكن المستخدم ، الذي يعمل مع هذه القائمة ، سيختبر تجربة ممتعة أكثر بكثير من ذي قبل. على الرغم من زيادة الوقت المطلوب لتقديم القائمة ، لن يلاحظها المستخدم. نحن ، دفعة واحدة ، نقدم 500 عنصر. في هذه الحالة ، يتم العرض خارج حدود الحاوية.

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

إليك ما يبدو عليه العمل مع هذه القائمة.


القائمة سريعة

ملخص


تقنيات التقديم اليدوية والتقدمية للقوائم الكبيرة مفيدة بالتأكيد في بعض الحالات. استخدمتها في الحالات التي لا يناسبني فيها التمرير الافتراضي لسبب ما.

بالنظر إلى ما سبق ، يمكننا القول أن التمرير الافتراضي في الغالب ، المبني على أساس مكتبة جيدة ، مثل Angular CDK ، هو أفضل طريقة لعرض قوائم كبيرة. ولكن إذا تعذر استخدام التمرير الظاهري لسبب ما ، فإن المطور لديه إمكانيات أخرى.

القراء الأعزاء! كيف تقوم بإخراج قوائم كبيرة في مشاريع الويب الخاصة بك؟


All Articles