الاستخدام المؤجل للوظيفة التوجيهية في الزاوي

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



كتبها نفس المطور. بالنسبة لي ، بدت مثل هذه المكتبة كحل مثالي للمشكلة. تحتوي مكتبة tippy.js على مجموعة واسعة من الميزات. بمساعدتها ، يمكنك إنشاء تلميحات الأدوات (عناصر تلميح الأدوات) ، والعديد من العناصر الأخرى. يمكن تخصيص هذه العناصر باستخدام السمات ؛ فهي سريعة ، ومكتوبة بقوة ، وتوفر إمكانية الوصول إلى المحتوى ، وتختلف في العديد من الميزات المفيدة الأخرى.

لقد بدأت بإنشاء توجيه مجمع لـ tippy.js:

@Directive({ selector: '[tooltip]' })
export class TooltipDirective {
  private instance: Instance;
  private _content: string;

  get content() {
    return this._content;
  }

  @Input('tooltip') set content(content: string) {
    this._content = content;
    if (this.instance) this.instance.setContent(content);
  }

  constructor(private host: ElementRef<Element>, private zone: NgZone) {}

  ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {
      this.instance = tippy(this.host.nativeElement, {
        content: this.content,
      });
    });
}

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

الآن سنستخدم تلميح الأدوات في قائمة كبيرة من 700 عنصر:

@Component({
  selector: 'my-app',
  template: `
    <ul>
      <li *ngFor="let item of data" [tooltip]="item.label">
         {{ item.label }}
      </li>
    </ul>
  `
})
export class AppComponent {
  data = Array.from({ length: 700 }, (_, i) => ({
    id: i,
    label: `Value ${i}`,
  }));
}

كل شيء يعمل كما هو متوقع. يعرض كل عنصر تلميح أداة. ولكن يمكننا حل هذه المشكلة بشكل أفضل. في حالتنا ، تم إنشاء 700 نسخة tippy. ولكل عنصر ، أضافت أدوات tippy.js 4 مستمعين للأحداث. هذا يعني أننا سجلنا 2800 مستمع (700 * 4).

لرؤية هذا بنفسك ، يمكنك استخدام الطريقة getEventListenersفي وحدة تحكم أدوات مطوري Chrome. تعرض بنية العرض getEventListeners(element)معلومات حول مستمعي الأحداث المسجلين لعنصر معين.


ملخص لجميع مستمعي الأحداث

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

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

قمIntersectionObserverبإنشاء الغلاف الذي يمثله الكائن المرصود:

const hasSupport = 'IntersectionObserver' in window;

export function inView(
  element: Element,
  options: IntersectionObserverInit = {
    root: null,
    threshold: 0.5,
  }
) {
  return new Observable((subscriber) => {
    if (!hasSupport) {
      subscriber.next(true);
      subscriber.complete();
    }

    const observer = new IntersectionObserver(([entry]) => {
      subscriber.next(entry.isIntersecting);
    }, options);

    observer.observe(element);

    return () => observer.disconnect();
  });
}

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

الآن inViewاستخدام الكائن المرصود في توجيه ينفذ وظيفة تلميح الأدوات:

@Directive({ selector: '[tooltip]' })
export class TooltipDirective {
  ...

  ngAfterViewInit() {
    //   
    inView(this.host.nativeElement).subscribe((inView) => {
      if (inView && !this.instance) {
        this.zone.runOutsideAngular(() => {
          this.instance = tippy(this.host.nativeElement, {
            content: this.content,
          });
        });
      } else if (this.instance) {
        this.instance.destroy();
        this.instance = null;
      }
    });
  }
}

شغّل الرمز مرة أخرى لتحليل عدد مستمعي الأحداث.


ملخص لجميع مستمعي الحدث بعد الانتهاء من المشروع

ممتاز. الآن نقوم بإنشاء تلميحات أدوات للعناصر المرئية فقط.

دعنا نبحث عن إجابات لبعض الأسئلة المتعلقة بالحل الجديد.

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

ماذا عن تفويض الحدث؟ للقيام بذلك ، تحتاج إلى تنفيذ آليات إضافية بنفسك ، في Angular لا توجد طريقة عالمية لحل هذه المشكلة.

ملخص


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

وكيف يمكنك حل مشكلة عرض قائمة كبيرة من العناصر ، كل منها يحتاج إلى تزويده بنص أداة؟

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


All Articles