mohammadsharifi.com
چگونه اپ انگولاری تون بهینهتر و کاراتر کنید؟
1. استفاده از OnPush
به شکل پیشفرض، انگولار تشخیص تغییر را روی تمامی کامپوننت ها با هر بار تغییر اجرا می کند __ برای مثال از رویداد کلیک گرفته تا اطلاعات دریافتی از یک درخواست ajax. (رویداد های کاربر، تایمر، xhr، promise و ...).
برای مثال بیاید تصور کنیم، که یک کامپوننت select داریم.
@Component({
selector: 'my-select',
template: `
<select (change)="change()">
<option *ngFor="let option of options" [value]="option.id"> {{option.name}}
</option>
</select>
`
})
export class MySelectComponent {
@Input() options = [];
}
ما می خوایم که یک آرایه از مهارت رو به کامپوننت select پاس بدیم و پراپرتی هایی رو تحت عنوان getter هایی برای اینکه بفهمیم انگولار کی مقدار اونا رو چک می کنه، سِت می کنیم.
class Skill {
constructor( private _id, private _name ) {}
get id() {
console.log('Checking id');
return this._id;
}
get name() {
console.log('Checking name');
return this._name;
}
}
@Component({
template:
` <my-select [options]="skills"></my-select>
<button (click)="trigger()">Trigger change detection</button>
`})
export class AppComponent {
skills = [ new Skill(1, 'JS'), new Skill(2, 'CSS'), new Skill(3, 'Angular') ];
trigger() {}
}
وقتی که دکمه رو فشار میدید، انگولار سیکل تشخیص تغییر رو اجرا می کنه. به دلیل اینکه در حالت توسعه هستیم برای هر بایندینگ این کار دو بار اتفاق می افته. خب برای مورد ما، محاسبات از این قراره:
option.id * 2
+
option.name * 2
*
3 options
=
12
با این حساب انگولار بسیار سریع است، و با رشد برنامه تون، انگولار باید سخت تر کار کنه که تمام تغییرات رو شناسایی کنه و چیزی از دستش در نره!
شاید با خودتون فکر کنید که چی می شد اگه انگولار یه راه بهتر رو برای اینکه تشخیص بده که کی کامپوننت رو چک کنه؟
بله، ما می تونیم ChangeDetectionStrategy کامپوننتمون رو به مقدار ChangeDetectionStrategy.OnPush
سِت کنیم. این به انگولار میگه که کامپوننت فقط وابستگی به Inputها داره و فقط در موارد زیر نیاز به چک شدن داره:
- تغییر ارجاع ()Input
- رخ داد یک رویداد داخل کامپوننت یا یکی از فرزندانش
- اجرای صریح تشخیص تغییر با
detectChanges() / tick() / markForCheck()
کد کاموننت بعد از تغییر:
@Component({
selector: 'my-select', template: `
... `,
changeDetection: ChangeDetectionStrategy.OnPush
})
به دلیل اینکه هیچ کدوم از شرایط بالا که گفتیم اتفاق نمی افتد، انگولار کامپوننت را در سیکل جاری تشخیص تغییر چک نمی کند. ای ولاا انگولار!
برای اینکه در عمل با جزییات کار آشنا بشید، به نظرم بهتره (بعد از خوندن این مقاله) که ویدیو زیر رو با دقت نگاه کنید و یکبار برای همیشه یاد بگیرید که چگونه اپ انگولاری تون رو بهینه تر کنید. مطمعنم از دیدنش لذت میبرید!
2. استفاده از TrackBy
خب در این مرحله (با انجام اقدام 1)، ما به اندازه که می توستیم بهبود ها رو انجام ندادیم. اگر در بعضی از جاها، ما نیاز به تغییر داده در یک کالکشن (this.skills) داشته باشیم، برای مثال داده هایی که پس از درخواست API بر می گرده ما به مشکل برمی خوریم، چرا که انگولار نمی تونه تمام آیتم های داخل یک کالکشن رو وارسی (track) کنه، و هیچ اطلاعی از اینکه آیتم ها حذف یا اضافه بشن نداره.
در نتیجه، انگولار نیاز داره که تمام عناصر DOM که مرتبط با داده هستند را حذف کند و دوباره آنها را بسازد. و این به معنی دستکاری زیاد DOM است، به ویژه برای مواردی که یک کالکشن بزرگ داریم. و ما می دونیم که دستکاری DOM کاری هزینهبر است.
بیاید در عمل ببینیم:
export class AppComponent {
skills = [ ... ] ngOnInit() {
setTimeout(() => {
this.skills = [ ...same skills as before, new Skill(4, 'Typescript') ]
}, 4000 );
}}
اگه ما از یک تابع trackBy استفاده کنیم، انگولار می تونه آیتم هایی که اضافه، حذف شدن رو برای یک شناسه یکتا وارسی کنه، و فقط اونا رو بسازه یا از بین ببره که تغییر پیدا کردن.
<select>
<option *ngFor="let skill of skills trackBy: skillById">{{ skill.name }}</option >
</select>
skillById(index: number, skill: Skill) {
return skill.id;
}
3. از انجام محاسبات در تمپلیت پرهیز کنید
بعضی وقتا نیازه که مقداری از سرور برگردونده شده را برای اینکه در رابط کاربری نشان بدیم نیاز به یک سری تبدیلات داره.
برای مثال:
@Component({
selector: 'skills',
template: `
<table>
<tr *ngFor="let skill of skills">{{skill.calcSomething(skill)}}</tr>
</table> `
})
export class SkillsComponent {
calcSomething(skill) { ... }
}
مشکل اینه که، چون انگولار نیاز به اجرای مجدد تابع تون در هر سیکل تشخیص تغییر داره، اگه تابع یک کار زمان بر رو انجام بده، برامون هزینه بردار خواهد بود.
اگه مقدار به شکل پویا در زمان اجرای برنامه تغییر نمی کند، یک راه حل بهتر می تونه:
- استفاده از پایپ (Pipe) های pure یا خالص باشه __ انگولار یک پایپ pure رو فقط وقتی که مقدار ورودی (Input) تغییر داشته باشه اجرا می کنه.
- یک پراپرتی جدید بسازید و یک بار مقدار جدید بهش بدید، برای مثال:
this.skills = this.skills.map(skill => ( { ...skill, percentage: calcSomething(skill) } );
4. تشخیص تغییرات را غیر فعال کنید
تصور کنید که شما یک کامپوننت دارید که وابسته به داده هایی هست که مدام در حال تغییر هستند، چندین بار در ثانیه.
به روزرسانی رابط کاربری وقتی که داده جدید برمی گردد می تونه بسیار گرون تموم شه. یک راه موثرتر می تونه چک کردن و به روزرسانی واسط کاربری مون در هر X ثانیه باشه.
ما می تونیم با جدا کردن (detach) تشخیصگر تغییرِ کامپوننت و به شکل محلی چک کردن رو هر x ثانیه اجرا کنیم.
@Component({
selector: 'giant-list',
template:
`
<li *ngFor="let d of dataProvider.data">Data {{d}}</lig>
`
})
class GiantList {
constructor(private ref: ChangeDetectorRef, private dataProvider:
DataProvider) {
ref.detach();
setInterval(() => {
this.ref.detectChanges();
}, 5000);
}
}
5. استفاده از Lazy Loading
به عقیده من، lazy loading یا بارگزاری تنبل وار یکی از قدرتمندترین ویژگی های انگولار به حساب میاد و هنوز کمتر استفاده میشه.
در حالت پیشفرض، وب پک ( Webpack ) تمام کدای اپتون رو در یک فایل بزرگ، باندل شده خروجی خواهد داد. بارگزاری تنبل وار به شما این قابلیت رو میده که برنامه تون در زمان بارگزاری بهینه کنید. این کار با جداسازی ماژول های feature تون و بارگزاری اونا در زمان مورد نیاز میسر میشه.
انگولار این فرآیند رو به شکلی غیرقابل محسوس اجرا می کند.
{
path: 'admin',
loadChildren: 'app/admin/admin.module#AdminModule'
}
همچنین ما می تونیم به کلی از بارگزاری ماژول ها بر اساس یک سری شرایط جلوگیری کنیم. برای مثال ماژول admin رو زمانی که کاربر مدیر (admin) نیست لود نکنید. (گارد canLoad رو مشاهده کنید.)
همه چیز اینجا تموم نمیشه! اما نکات مهم رو گفتم براتون. برای مشاهده موارد بیشتر از جمله سرویس ورکر می تونید به چک لیست پرفورمنس انگولار نگاهی بیندازید.
به جامعه انگولار ایران بپوندید!
انگولار ایران جامعه بزرگ از افراد مشتاق به رشد و پیشرفت خودشون هستند و علاقه مند به فریم ورک انگولار هستند. خیلی خوشحال میشیم شما هم به جمع مون اضافه بشید:
مطلبی دیگر از این انتشارات
چه سایت هایی با ریاکت یا ویو یا انگولار نوشته شدن؟
مطلبی دیگر از این انتشارات
شروع کار با فریمورک انگولار در ویرایشگر VS Code
مطلبی دیگر از این انتشارات
تفاوت فریم ورک های جاوا جان اسکریپت!