پرفورمنس در انگولار

نویسنده: فاطمه نبی زاده -سپیده چایچی - جواد رسولی

5 دقیقه زمان مطالعه
1401/02/06
بدون دیدگاه

آیا برنامه‌هایی که با انگولار نوشته‌اید، کند است؟ در این مقاله قصد داریم روش‌هایی برای بهبود عملکرد یا پرفورمنس پروژه‌های انگولاری به شما معرفی کنیم. 

انگولار به طور پیشفرض یک چارچوب سریع و کارآمد است. سرعت بالای توسعه و کد‌نویسی، سرعت بالای لود صفحات و سادگی در نوشتن نسخه موبایل و تبلت، از مزایایی است که انگولار را را متمایز می‌کند. با همه این اوصاف باز هم روش‌های مختلفی برای بهبود پرفورمنس در انگولار وجود دارد. هنگام نوشتن برنامه‌های مهم با محتوای بزرگ و پیچیده یا برنامه‌هایی که به طور مداوم بروزرسانی می‌شوند، همیشه احتمال بوجود آمدن مشکلات وجود دارد. منابع و مقالات زیادی در مورد بهبود عملکرد با برنامه‌های انگولار منتشر شده است. در حالی‌ که بیش‌تر آن‌ها توصیه‌های خوبی و معتبری هستند؛ اما در آن‌ها درباره کاهش مشکلات عملکردی که در واقعیت با آن‌ها مواجه خواهیم شد، صحبتی نشده است. در این مقاله، قصد داریم برخی از مهم‌ترین دلایل کند بودن برنامه‌های انگولار و روش‌های بهبود پرفورمنس در انگولار را بیان کنیم. 

برای بهبود پرفورمنس در انگولار از کجا شروع کنیم؟

مشکلات پرفورمنسی، به طور معمول بعد از آشکار شدن، بر تجربه کاربران نهایی تاثیر می‌گذارند. مواردی مانند نرخ ترافیک، کاهش تعامل کاربر با برنامه‌های شما و نرخ پرش زیاد، از عواملی است که می‌تواند به عنوان یک هشدار برای شما باشد تا مشکلات عملکردی برنامه و پرفورمنس در انگولار بهبود دهید. در مواردی از این‌ دست، هدف ما باید شناسایی مشکل و سپس تلاش بر روی بهینه‌سازی آن باشد.

برخی از مشکلات رایجی که در برنامه‌ها با آن مواجه هستیم شامل موارد زیر است:

  • استفاده غیر ضروری از منابع سرور
  • کندی صفحات
  • کند شدن‌های دوره‌ای در برنامه
  • عدم مطابقت نتایج مورد انتظار با فناوری استفاده شده در برنامه 
  • خطاهای غیرمنتظره در برنامه یا در زمان اجرا

این مشکلات با استفاده از تکنیک‌های بهینه‌سازی در انگولار قابل اصلاح می‌باشند. اما اولین چیزی که باید از انجام آن مطمئن شوید این است که برنامه شما به اصول معماری کدگذاری تمیز (Clean Code) پایبند باشد. در این بخش، مواردی را مطرح می‌کنیم که در انگولار استفاده شده و می‌توانند تأثیر چشمگیری در بهبود عملکرد برنامه و پرفورمنس در انگولار شما ایجاد کنند.

دو نوع بهینه‌سازی برای پرفورمنس در انگولار قابل پیاده‌سازی است:

  • بهینه‌سازی زمان اجرا
  • بهینه‌سازی زمان لود

در این مقاله، ما به راه‌حل‌های بهینه‌سازی در زمان اجرا می‌پردازیم. مرورگر کروم مجموعه‌ای از ابزارها را برای بررسی و ارزیابی عملکرد برنامه یا صفحه وب به شما ارائه می‌دهد. اگر چه این ابزارها به برطرف‌ کردن نواقص برنامه، کمک زیادی می‌کنند اما برنامه شما هنوز هم برای مشکلات اساسی خود نیاز به اصلاحات زیادی دارد. در ادامه به چند روش برای بهبود عملکرد برنامه در زمان اجرا در انگولار اشاره شده است.

تشخیص تغییر (Change Detection)

تشخیص تغییر در پرفورمنس در انگولار مکانیزمی قدرتمند است که می‌تواند به شما در بهینه‌سازی معیارهای عملکرد کمک کند. تغییر تشخیص در Angular هنگامی اتفاق می‌افتد که شما هر یک از مدل‌های خود را تغییر دهید، انگولار این تغییرات را تشخیص داده و بلافاصله View را بروزرسانی می‌کند. این اتفاق تشخیص تغییر در انگولار است. هدف اجرای مکانیزم این است که مطمئن شوید Viewهای زیرین همیشه با مدل‌های مربوطه خود sync هستند. این ویژگی اصلی Angular تا حدی زیادی دلیل انتخاب Angular به عنوان یک چارچوب مناسب برای توسعه برنامه‌های وب مدرن است. یک مدل در Angular می‌تواند به دلیل اتفاق افتادن هر یک از سناریوهای زیر تغییر کند:

  • رویدادهای DOM (کلیک، شناور و …)
  • درخواست‌های AJAX
  • مقادیر @Input بروزرسانی شده در کامپوننت

 بااین‌حال، وقتی صحبت از برنامه‌های پیچیده در مقیاس بزرگ و پرفورمنس در انگولار می‌شود، تشخیص تغییر می‌تواند به‌عنوان یک چالش مطرح شود. به طور پیش‌فرض، انگولار تشخیص تغییر را روی تمامی کامپوننت‌ها (همچنین کامپوننت‌های فرزند) حتی اگر تغییرات پدر بر آن‌ها تاثیری نداشته باشد؛ با هر بار تغییر اجرا می‌کند. برای مثال از رویداد کلیک گرفته تا اطلاعات دریافتی از یک درخواست ajax (رویدادهای کاربر، تایمر، xhr، promise و …). با تشخیص تغییرات پیش‌ فرض در برنامه‌های انگولار، رندرینگ برای کل کامپوننت‌ها و child های آن‌ها اتفاق می‌افتد. این پروسه، برنامه را کند می‌کند و منجر به زمان انتظار طولانی‌تر و تجربه بد کاربر می‌شود. در چنین مواردی، ما می‌توانیم با استفاده از این سه استراتژی آشکار تشخیص تغییر، فرایند پرفورمنس در انگولار بهینه کنیم:

  • OnPush & Immutability
  • جداکردن تغییر تشخیص (Detach change Detection)
  • استفاده از Pure Pipe ها 

  در ادامه، به توضیح هر روش می‌پردازیم.

OnPush & Immutability

به‌ منظور رفع مشکل فوق، ما با استراتژی تشخیص “onPush” کار می‌کنیم. اگر کامپوننت والد در حال بروزرسانی مقادیری است که به‌ عنوان ویژگی “Input” به کامپوننت Child  منتقل نشده است، در نتیجه کامپوننت فرزند نباید رندر شود.  اطلاعات بیش‌تر درباره این مطلب را می‌توانید  اینجا را ببینید.

مانند کد زیر OnPush  را ست می‌کنیم:

@Component({
     selector: 'child-component',
     changeDetection: ChangeDetectionStrategy.OnPush,
     template: `...`
})

اگر تصمیم بگیرید که از استراتژی OnPush در یک کامپوننت انگولاری استفاده کنید، اعمال Immutable ایده خوبی است. با استراتژی OnPush، ما آبجکت‌ها را به صورت مستقیم تغییر نمی‌دهیم. در عوض ما یک آبجکت جدید (که البته دارای reference جدید است) ایجاد می‌کنیم تا استراتژی OnPush خود باعث ایجاد تغییرات شود.

به صورت معمول ساختارهای داده تغییرناپذیر به‌گونه‌ای طراحی می‌شوند، که عملیات مشترک را به سرعت انجام دهند. هنگامی که تغییری ایجاد می‌شود؛ نسخه جدیدی از ساختار داده با مراجعه به نسخه قدیمی یا قسمتی از آن و افزودن اطلاعات در مورد آنچه تغییر کرده است، ایجاد می‌شود. بیش‌تر ساختار قبلی در ساختار جدید استفاده می‌شود. این موضوع به‌ عنوان اشتراک ساختاری شناخته می‌شود. همچنین به این معنی است، که همه مقادیر به‌ صورت فیزیکی در نسخه جدید کپی نشده‌ است، بنابراین این حافظه بسیار کارآمدتر از ایجاد یک نسخه کامل خواهد بود. اگر علاقه دارید بیشتر در این باره بدانید می‌توانید به این لینک مراجعه کنید.

جداکردن تغییر تشخیص (Detach change Detection)

یک راه تهاجمی‌تر برای کاهش بررسی یک کامپوننت و زیرمجموعه‌های آن، جداکردن ردیاب تغییر از کامپوننت است:

با جداکردن ردیاب تغییر می‌توانید از بررسی کامپوننت و زیرمجموعه‌های آن که  نیازی به بررسی تغییرات نیست جلوگیری کنید. این کار با استفاده از ChangeDetectorRef انجام می‌شود. هر زمان که تمایل داشته باشیم، می‌توانیم با استفاده از نمونه ChangeDetectorRef تشخیص تغییر را دوباره به لیست بررسی متصل کنیم.

با نمودار زیر می‌توانید سناریوی بالا را درک کنید:

در تصویر بالا انگولار نمی‌تواند کامپوننت Count و اجزای زیرمجموعه‌اش را بررسی کند و حتی نمی‌تواند View را بروزرسانی کند و Binding را انجام دهد؛ زیرا ردیاب تغییر آن جدا شده بوده و می‌توان با اکشن دیگری دوباره این کامپوننت را به ردیاب تغییر متصل کرد. (reattach()).

class TestComponent {
  constructor(private changeDetectorRef: ChangeDetectorRef) {
      changeDetectorRef.detach()
   }
   clickHandler() {
       chnageDetectorRef.detectChnages()
   }
}

استفاده از Pure Pipe ها 

در انگولار، از Pipe ها برای فرمت دیتاها استفاده می‌شود. مثلاً ممکن است تاریخ تولد به‌ صورت میلادی از سرور دریافت شده باشد، می‌خواهیم بدون تغییری در متغیر حامل تاریخ میلادی و فقط در لایه رابط کاربری، کاربر تاریخ را به‌ صورت شمسی مشاهده کند. به‌ عبارت‌ دیگر برای تغییر نحوه نمایش مقدار نمایشی (display-value) در صفحات HTML خود، از Pipe استفاده می‌کند. به صورت کلی Pipe ها در دو دسته Pure و Impure قرار می‌گیرند. تفاوت این  Pipe ها در زمان فراخوانی دوباره آنها است. یک Pipe  در انگولار به‌صورت پیش‌فرض Pure است. این نوع Pipe ها تنها زمانی فراخوانی مجدد می‌شوند که یک تغییر محض (Pure Change) بر روی عبارت ورودی آنها رخ دهد. هر نوع تغییری بر روی عبارات ورودی از جنس string ، number ، Boolean ، Symbol و عبارات اولیه، یا هر نوع تغییری در ارجاع یک شی مانند  Date Array ، Function و Object نیز تغییر محض محسوب می‌شود. به‌ عنوان‌ مثال هیچ‌کدام از تغییرات زیر یک تغییر محض محسوب نمی‌شوند:

numbers.push(10);
obj.name = ‘javad’;

زیرا با اضافه‌شدن عنصری به یک آرایه یا تغییر خصوصیتی از یک شی، باعث ایجاد تغییری در ارجاع آنها نمی‌شود و همان‌ طور که اشاره شد، در عباراتی از نوع آرایه و Object، فقط تغییر در ارجاع آن‌ها یک تغییر محض محسوب می‌شود.

حالا می‌توان به این نتیجه رسید که اضافه‌ شدن مقدار به آرایه یا بروزرسانی یک property از object، باعث فراخوانی مجدد Pure Pipe نخواهد نشد. شاید این نوع از Pipe ها محدودکننده باشند، اما بسیار سریع هم هستند (بررسی تغییر در ارجاع یک شی بسیار سریع‌تر از بررسی کامل یک شی صورت می‌گیرد). 

 Impure Pipe ها در اغلب رخدادهای کامپوننت از جمله فشرده شدن کلید یا حرکت ماوس و رخدادهای دیگر نیز، فراخوانی مجدد می‌شوند. با در نظر گرفتن این نگرانی، هنگام پیاده‌سازی این نوع Pipe ها باید مراقب بود؛ زیرا این نوع Pipe ها با اجرای طولانی خود، می‌توانند رابط کاربری شما را نابود کنند.

Web Worker

web worker یکی از فناوری‌های تحت وب شناخته می شود که توسط W3C ارائه شده است. وب ورکر به شما اجازه می‌دهد تا بتوانید عملیاتی را که نیاز به زمان زیادی برای پردازش دارد، در پشت‌ صحنه انجام دهید؛ بدون این که وقفه‌ای در پردازش UI ایجاد شود. وب ورکر حتی به شما اجازه می‌دهد چند thread را هم‌ زمان اجرا کنید و پردازش‌هایی موازی یکدیگر داشته باشید. از آن جایی که وب ورکرها یک ترد پردازشی جدا از UI به‌ حساب می‌آیند، شما دسترسی به DOM ندارید؛ ولی می‌توانید از طریق ارسال پیام، با صفحه وب تعامل داشته باشد. پیاده‌ سازی جاوا اسکریپت در همه مرورگرها به‌ صورت تک thread است و بدین ترتیب کل برنامه بر روی یک thread اجرا می‌شود. اجرای تک thread به طور چشمگیری نرخ فریم برنامه پیچیده را کاهش می‌دهد؛ زیرا هم UI و هم اجرای JS توسط یک thread انجام می‌شود. از آن جایی که انگولار به طور پیش‌فرض از دست‌کاری مستقیم DOM جلوگیری می‌کند، می‌توان کل برنامه انگولار را در یک web worker thread جداگانه اجرا کرد بنابراین thread اصلی را آزاد نگه می‌دارد تا فقط رندر UI را اداره کند. با این‌ حال، بسته‌های npm زیادی وجود دارند که سعی می‌کنند، به صورت مستقیم به DOM دسترسی پیدا کرده و در نتیجه تمام برنامه را در فرایند worker اجرا کنند. 

Trackby 

خیلی وقت‌ها پیش می آید که شما در وب اپلیکیشن‌های انگولار نیاز پیدا می کنید تا روی یک Collection پیمایش انجام دهید و برای نمایش اعضای آن از دستور ngFor استفاده می‌کنید که یک الگو را یک‌بار در هر مورد از مجموعه ایجاد می‌کند. 

حال اگر زمانی ما نیاز به تغییر داده‌های این مجموعه داشته باشیم، به‌ عنوان‌ مثال در نتیجه درخواست یک API، یا تعاملی که کاربر با برنامه خواهد داشت و موردی را حذف کند یا چیزی را اضافه کند، ما با یک مشکل روبرو هستیم! چون انگولار نمی‌تواند موارد موجود در مجموعه را ردیابی کند و هیچ اطلاعی از این که کدام موارد حذف یا اضافه شده‌اند را ندارد. در نتیجه، باید تمام عناصر DOM مربوط به داده‌ها را حذف کرده و دوباره آن ها را ایجاد کند. این یعنی در بسیاری از دست‌کاری‌های DOM به خصوص در مورد مجموعه بزرگی از داده‎ها، این دستکاری‌های DOM می‌تواند خیلی هزینه بر و گران باشند و در حقیقت عملکرد و کارایی برنامه شما را کاهش می دهند. ما می‌توانیم با ارائه یک تابع trackBy، به انگولار کمک کنیم مواردی که اضافه یا حذف می‌شوند را پیگیری کند. تابع trackBy در واقع index و item فعلی را به‌ عنوان آرگومان ورودی در نظر می‌گیرد و باید یک شناسه منحصر به‌ فرد را برای این item برگرداند.


@Component({
  selector: 'app', 
  template: `<ul>
                  <li *ngFor="let item of items; trackBy: trackById">
			{{item.name}}  
		</li>         
       </ul>`
})
class AppComponent {
  Items = [
      {
          id: 1,
          name: 'item 1'
      }, {
          id: 2,
          name: 'item 2'
      },
      ...
  ];
  trackById(index, item) {
      return item.id;
  }
}

فعال‌سازی  enableProdMode

با فراخوانی ()enable Prod Mod در انگولار، از انجام بررسی‌های بیشتر برای تشخیص تغییرات جلوگیری کنید.

import {enableProdMode} from '@angular/core';
if (ENV === 'production') {
    enableProdMode();

کامپایلر Ahead-of-Time ) AoT)

زمانی که همه کدها یک جا به زبان ماشین تبدیل می‌شوند؛ قبل از این که به پلتفرمی که آن را اجرا می‌کنند، برسند، به آن Ahead Of Time (به معنی جلوتر از زمان) یا به‌ صورت مخفف کامپایل کردن AOT گفته می‌شود. یعنی برنامه قبل از این که اجرا شود، کاملاً کامپایل شده و به کدهای ماشین تبدیل می‌شود و سپس برای اجرا در اختیار پلتفرم قرار می‌گیرد. قبل از این که مرورگر، برنامه را بارگیری کند، کامپایلر AoT انگولار کد HTML و TypeScript را به کد JavaScript تبدیل می‌کند و فرایند رندر سریع‌تری را فراهم می‌کند. انگولار، دو روش جهت کامپایل، ارائه می‌دهد:

Just-in-Time) JIT) 

JIT در هنگام اجرا، برنامه شما را در مرورگر، کامپایل می‌کند. به‌صورت پیش‌فرض تا انگولار ورژن ۸ از این روش استفاده می‌شود.

ng build

ng serve

enter image description here

Ahead-of-Time) AOT) 

AOT که هنگام ساخت، برنامه شما را کامپایل می‌کند. به‌صورت پیش‌فرض در انگولار ورژن ۹  و بعد از آن، از این روش استفاده می‌شود.

ng build –aot

ng serve –aot

enter image description here

مثال زیر، تفاوت زمان تعاملی بین دو برنامه Angular را نشان می‌دهد؛ که یکی از آنها  با AoT ساخته شده است و دیگری بدون آن.

AoTvsJITCompilation

همان‌طور که می‌بینید در این جا لود فایل‌های JS با استفاده از حالت JIT  به میزان ۸۹٪ بیش‌تر است و تفاوت فاحشی نسبت به برنامه کاربردی که از AoT استفاده می‌کرد وجود دارد. کامپایلر AoT  پکیج‌های سنگین جاوا اسکریپتی مانند vendor.bundle.js را حذف می‌کند؛ در نتیجه حجم دانلود فایل انگولار در آن کمتر خواهد شد و نسخه کامپایل شده را پیش بارگیری می‌کند؛ و زمان رندر سریع‌تری را صورت می‌دهد. هم چنین تعداد درخواست‌های غیر هم‌زمان را بهینه و امنیت بیشتری را تضمین می‌کند.

Webassembly 

 Webassemble یک مجموعه سطح پایین است؛ مانند زبانی که عملکرد بومی دارد. WebAssembly قصد دارد با بهره گیری از قابلیت های سخت افزاری رایج موجود در طیف گسترده ای از سیستم‌عامل‌ها را با سرعت بومی اجرا کند. شما می‌توانید از مزیت Webassembly برای اجرای مقداری از کد برنامه وب خود با Webassembly استفاده کنید. استفاده از جاوا اسکریپت ساده و با وجود فریمورک‌های مدرن در آن بسیار سریع است. اما استفاده از آن برای اپلیکیشن‌های اختصاصی سریع نیست. مانند اپلیکیشن‌هایی که در آن‌ها محاسبات زیادی انجام شده است. در این جا می‌توان از Web Assembly استفاده کرد. Assembly Web در اپلیکیشن‌هایی مثل برنامه‌های ویرایش تصویر، ساخت بازی و…  قابل استفاده است. با این‌ حال، باید توجه داشته باشید که wasm هنوز جدید است و استفاده از آن گاهی اوقات ممکن است، مشکل باشد زیرا در حال حاضر فقط ۴ نوع داده پشتیبانی شده (۲ بخش اعشاری و ۲ بخش صحیح) دارد. همچنین در حال حاضر ، در بیشتر موارد ، مزایای عملکرد wasm زمانی که از آن برای اجرای یک قطعه کوچک کد، با هنگامی که در مقایسه با JS داخلی استفاده می‌کنید، چندان زیاد نیست. شما باید به درستی ارزیابی کنید که کدام کد را می‌خواهید به wasm منتقل کنید.

Optimize Events

باید در نظر داشت که رویداد DOM تا زمانی که کامل نشود؛ تشخیص تغییر را بلاک می‌کند. به طور مثال اگر یک کلیک در کامپوننت خود دارید که آن کامپوننت برای سرویس دادن به آن، متد سرویس دیگری را فرامی‌خواند تا زمانی که کنترل از سرویس برنگردد؛ تشخیص تغییر کامل نمی‌شود. اگر سرویس شما زمان بیشتری را برای انجام عملیات مورد نظر، نیاز دارد؛ در نهایت تشخیص تغییرات را کند می‌کند. بهتر است در صورت امکان سعی کنید منطق سرویس خود را به WebWorker جداگانه ای منتقل کرده یا در صورت نیاز از wasm استفاده کنید. 

Unsubscribing Observables

Observable می‌تواند مشکل نشت حافظه ایجاد کند بنابراین زمانی که نیازی به آن نیست، بهتر است unsubscribe شوند. با این‌ حال شما مجبور نیستید همه observableهای استفاده شده را، unsubscribe کنید. زمانی unsubscribe کردن نیاز است؛ که یک Subscription در داخل یک کامپوننت ایجاد شده، که قبل از تکمیل Observable از بین رفته است.

یکی از راه‌هایی که شما می‌توانید برای unsubscribe  کردن Observable  در کامپوننت مور دنظر استفاده کنید کد زیر است:

ngOnDestroy() 
{
    this.subscription.unsubscribe()
}

Observable share() operator

اگر observable را در چند مکان/کامپوننت subscribe کردید؛ هر subscription سعی می‌کند، داده را هرچند تکراری تولید کند. ما می‌توانیم از پردازش داده‌های تکراری در subscription ها با استفاده از عملگر share() جلوگیری کنیم. برای  مثال نمونه زیر را در نظر بگیرید.

import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import {HttpClient} from "@angular/common/http"
 
@Injectable()
 
export class AppService{
data: Observable <any>;
constructor(private http:HttpClient){
    this.data= this.http.get<any> ('apiUrl').pipe(share());
    
}
getData(){
    return this.data;
}
}

Updating Third Party Packages

 هنگامی که روی پروژه خود کار می‌کنید، اغلب اوقات نیازمند استفاده از یک کتابخانه غیر انگولاری یا پلاگین هستید. برنامه‌های Third party، برنامه‌های مستقلی هستند که دارای پلاگین‌هایی است، که قابلیت‌های متفاوتی را به برنامه‌ها اضافه می‌کنند. پلاگین‌ها برای برنامه‌های FirstParty است، که در نرم‌افزارهای متن‌باز بکار برده می‌شوند. شرکت‌های خاص کد منبع سیستم‌عامل‌ها را در اختیار Third party یا مردم قرار نمی‌دهند و برای نوشتن پلاگین و افزونه‌ها دارای محدودیت هستند. برنامه‌های زیادی برای Third party وجود دارند که می‌توانند برای رشد پلاگین و افزونه‌ها از این برنامه‌ها که به‌صورت رایگان در اینترنت موجود است، استفاده کنند. برنامه‌های سیستم کامپیوتری مرورگرها مثل اپرا، فایرفاکس، برنامه‌های چندرسانه‌ای و فایروال‌ها، برنامه‌های Third party هستند.

بروزرسانی مرتب بسته‌های Third Party شما ممکن است منجر به عملکرد بهتر زمان اجرا شود.

console.log()

استفاده از دستورات console.log در کد شما می‌تواند ایده بدی باشد، زیرا باعث کند شدن عملکرد برنامه می‌شود و هم چنین ورود آبجکت با console.log، مشکل نشت حافظه ایجاد می‌کند. هنگامی که کنسول مرورگر باز است، با اجرای هر بار console.log  سرعت کندتر می‌شود و بر عملکرد سایت تأثیر قابل‌توجهی می‌گذارد. بهتر است دستورات console.log را به طور کامل از کد خود حذف کنید یا حداقل یک ورود شرطی به محیط خاص در نظر بگیرید.

Global Variables

استفاده از متغیرهای سراسری، معایب زیادی دارد که یکی از آنها نشت حافظه است. متغیرهای تعریف شده در دامنه سراسری تا زمانی که پنجره دوباره لود یا تب بسته شود، پاک نمی‌شوند در نتیجه، در صورت استفاده از متغیر سراسری، در تمام برنامه، منجر به نشت حافظه می‌شود. اگر به دلایلی می‌خواهید متغیرهای سراسری داشته باشید، روش‌های بهتری برای انجام آن در انگولار وجود دارد.

یکی از این روش‌ها قرار دادن متغیر سراسری در یک فایل (به نام global) جداگانه است. مانند مثال زیر:

//
// ==== File global.ts
//
 
'use strict'
 
export const sp='/';
export const version: string="22.2.2"

حال فایل global را در فایل‌های دیگر با عبارت Import استفاده می‌کنیم.

import * as myGlobals from 'globals';

برای مثال:

import { Component,OnInit } from "@angular/core";
import * as myGlobals from 'global';
 
export class HeroComponent  implements   OnInit 
{
public helloString: string ="hello "+myGlobals.sp + "there"
}

Event listeners

افزودن Event Listener به DOM می‌تواند مشکل نشت حافظه را (MemoryLeak) ایجاد کند. اگر فراموش کنید که listener را در رویداد $destroy از دایرکتیو خود حذف کنید، حتی اگر از سند حذف شود، باز هم به یک DOM اشاره می‌کند. درخت DOM سپس به یک “درخت DOM جدا ” تبدیل می‌شود و نشت می‌کند. موتورهای JS مدرن می‌توانند بیشتر این موقعیت‌ها را برای شما رقم بزنند و Listener را حذف کنند، اما طبقه‌بندی درختی پیچیده‌تر می‌تواند حتی بهترین Garbage Collection را به چالش بکشد.

EventListener به طور پیش‌فرض محدودیت اضافه‌کردن ۱۰ Listener در هر رویداد را دارد. هنگامی که بیش از ۱۰ Listener اضافه می‌شود، هشدار برای نشت احتمالی حافظه به ما نشان داده می‌شود. 

از روش emitter.listenerCount) eventName) می‌توان برای بررسی میزان Listener اضافه شده به رویداد استفاده کرد. این متد، عدد صحیحی برای نام رویدادی که به‌ عنوان آرگومان ارسال شده است، برمی‌گرداند.

از روش emitter.listeners) eventName) می‌توان برای اطلاع از جزئیات Listener هایی که در آنها مشترک است استفاده کرد. این روش مجموعه‌ای از Listener ها را برای نام رویدادی که در آرگومان ارسال شده است، بازمی‌گرداند.

نمونه EventEmitter یک متد emitter.setMaxListener) n) را در اختیار ما قرار می‌دهد که می‌تواند برای تغییر محدودیت یک نمونه خاص استفاده شود. ما می‌توانیم، مقدار n را برای تعدادی رویداد که می‌خواهیم اجازه دهیم، ارسال کنیم. مقدار را می‌توان روی Infinity یا ۰ برای اجازه به تعداد نامحدودی از Listenerها تنظیم کرد.

پس از تعیین محدودیت، می‌توانیم از emitter.getMaxListeners() برای بدست‌آوردن حداکثر مقدار Listener فعلی، برای نمونه emitter فعلی استفاده کنیم. در صورتی که محدودیت تعیین نشده باشد؛ مقدار پیش‌فرض برمی گردد.

این روش‌ها به ما کمک می‌کند نشت احتمالی حافظه را بررسی کرده و از اتفاق افتادن آن ها، جلوگیری کنیم.

Bad Third Party Packages

اگر بسته Third Party دارای مشکلات عملکرد (نشت حافظه، اجرای پرهزینه js ، امنیت و…) باشد. در نهایت بر عملکرد برنامه شما تأثیر می‌گذارد؛ بنابراین همیشه توصیه می‌شود قبل از استفاده، هر بسته Third Party را به‌ درستی بررسی کنید. 

جمع بندی

مواردی که در این مقاله بیان شد بخشی از روش‌هایی است که به بهبود پرفورمنس در انگولار، در زمان اجرای کمک می‌کند. همه نکاتی که در این مقاله به آن‌ها اشاره شد؛ باید برای نوشتن یک برنامه انگولار با عملکرد بالا رعایت شوند. علاوه بر این، شما باید مطمئن شوید که عملکرد و آنالیز مناسب برنامه خود را با استفاده از ابزارهای مختلف موجود مانند Chrome/Edge/Firefox JavaScript Profiling tool ، Heap snapshots comparison ، Chrome Lighthouse و … بسنجید؛ تا قضاوت مناسبی در مورد علت دقیق مشکلات احتمالی داشته باشید.

منابع:

  1. https://faun.pub/44-quick-tips-to-fine-tune-angular-performance-9f5768f5d945
  2. https://medium.com/swlh/angular-performance-optimization-techniques-5b7ca0808f8b
  3. https://angular.io/guide/aot-compiler
  4. https://blog.bitsrc.io/top-reasons-why-your-angular-app-is-slow-c36780a0a289
  5. https://www.simform.com/angular-performance
  6. https://stackoverflow.com/questions/41450226/just-in-time-jit-vs-ahead-of-time-aot-compilation-in-angular/52302097#52302097
  7. https://www.code-sample.com/2018/04/angular-ahead-of-time-aot-compiler.html
  8. https://indepth.dev/posts/1381/immutability-importance-in-angular-applications
  9. https://medium.com/preezma/memory-leaks-in-javascript-and-how-to-avoid-them-63916a02f68
  10. https://www.dntips.ir/post/2680/pipe%d9%87%d8%a7-%d8%af%d8%b1-angular-2-%d8%a8%d8%ae%d8%b4-%d8%a7%d9%88%d9%84
  11. https://virgool.io/@mahreza/%D8%A8%D9%87%D8%A8%D9%88%D8%AF-%D8%B9%D9%85%D9%84%DA%A9%D8%B1%D8%AF-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86%D9%87%D8%A7%DB%8C-angular-2-%D8%A8%D8%A7-trackby-blrwvpkzyfxn
  12. https://angular.io/guide/aot-compiler
  13. https://www.developintelligence.com/blog/2016/11/avoiding-memory-leaks-in-javascript/
  14. https://www.telerik.com/blogs/simplifying-angular-change-detection#:~:text=Detach%20the%20Change%20Detector&text=You%20can%20avoid%20checking%20the,view%20and%20perform%20the%20bindings.