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