۲۰ نکته درباره Angular Best Practices در سال ۲۰۲۳

فریم ورک Angular که توسط گوگل توسعه یافته است، یک چارچوب قدرتمند برای برنامه نویسی پویای Single Page Application بر اساس TypeScript است.این یک چارچوب ساختاریافته است نه یک کتابخانه مانند React ، بنابراین توسعه دهندگان باید مثل Angular « فکر کنند ». در پشت صحنه، از بلوک‌های سازنده زیادی استفاده می‌کند: ماژول‌ها، کامپوننت ها، data binding، سرویس ها، directivesها و موارد دیگر. این مقاله می خواهد بهترین شیوه های Angular را برای اجرای بهینه با رعایت قوانین ساده توضیح دهد.1. از Angular CLI برای راه اندازی پروژه استفاده کنید.چه : همیشه از Angular CLI برای ایجاد و مدیریت پروژه های Angular خود استفاده کنید.چرا : Angular CLI یک راه استاندارد و کارآمد برای bootstrap، توسعه، آزمایش و استقرار برنامه های Angular ارائه می دهد.چگونه :# Install Angular CLI globally (if not already installed) npm install -g @angular/cli # Create a new Angular project ng new my-angular-app # Navigate to the project directory cd my-angular-app # Generate a component ng generate component my-component # Run the development server ng serve2. از اصل Single Responsibility یا (SRP) پیروی کنید.چه : هر کامپوننت، سرویس یا ماژول باید یک مسئولیت واحد داشته باشد.چرا : SRP قابلیت نگهداری، خوانایی و استفاده مجدد کد را ارتقا می دهد. درک و اشکال زدایی کد شما را نیز آسان تر می کند.چگونه :// Example of a component with a single responsibility import { Component } from '@angular/core'; @Component({ selector: 'app-user', template: '<div>{{ user.name }}</div>', }) export class UserComponent { user = { name: 'Marco Martorana' }; }3. بهینه سازی عملکرد با OnPush Change Detection.چه : از ChangeDetectionStrategy.OnPushبرای بهینه سازی change detection در کامپوننت ها استفاده کنید.چرا : change detection OnPush تعداد بررسی ها را کاهش می دهد و عملکرد را به خصوص در برنامه های بزرگ افزایش می دهد.چگونه :import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'app-user', template: '<div>{{ user.name }}</div>', changeDetection: ChangeDetectionStrategy.OnPush, }) export class UserComponent { user = { name: 'Marco Martorana' }; }4. از Reactive Forms بیشتر استفاده کنید.چه : از فرم‌های Reactive انگولار همراه با form validationها استفاده کنید.چرا : Angular دو نوع مختلف فرم را ارائه می دهد: Template Driven و فرم های Reactive . یک فرم Reactive روشی با قابلیت پیش بینی و مدیریت بیشتری برای مدیریت داده های فرم، از جمله اعتبار سنجی و عملیات async ارائه می دهد. علاوه بر این فریم ورک Angular از کتابخانه RxJS استفاده می کند، که بر اساس برنامه نویسی reactive است و فرم های Reactive از این شیوه استفاده می کند.چگونه :import { Component } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-registration', template: ` <form [formGroup]=&quotregistrationForm&quot (ngSubmit)=&quotonSubmit()&quot> <input formControlName=&quotusername&quot placeholder=&quotUsername&quot /> <button type=&quotsubmit&quot [disabled]=&quotregistrationForm.invalid&quot>Register</button> </form> `, }) export class RegistrationComponent { registrationForm: FormGroup; constructor(private fb: FormBuilder) { this.registrationForm = this.fb.group({ username: ['', [Validators.required, Validators.minLength(5)]], }); } onSubmit() { // Manage submission here } }5. ماژول های Lazy Loadچه : lazy load را برای ماژول های feature فعال کنید تا آنها را در زمان نیاز بارگیری کنید.چرا : lazy load حجم اولیه بسته برنامه را کاهش می دهد و سرعت بارگذاری اولیه برنامه را افزایش می دهد. این ماژولارسازی عملکرد را بهبود می‌بخشد.چگونه :import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule), }, // Other routes... ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], }) export class AppRoutingModule { }6. از TrackBy با ngFor Directive استفاده کنید.چه : یک تابع برای trackBy در حلقه های ngFor بنویسید.چرا : عملکرد رندرینگ را برای لیست ها بهینه می کند.چگونه :// In component trackByFn(index: number, item: any): number { return item.id; } <!-- In template --> <div *ngFor=&quotlet item of items; trackBy: trackByFn&quot>{{ item.name }}</div>7. مستندسازی کدچه چیزی : کامنت های معنی دار اضافه کنید و کد خود را مستند کنید.چرا : خوانایی کد را بهبود می بخشد و به توسعه دهندگان دیگر کمک می کند تا کد شما را درک کنند.چگونه :/** * This is an sample function that print a message in console and return length of input parameter. * @param {string} param - Description of the parameter. * @returns {number} Description of the return value. */ function helloFunction(name: string): number { // Function logic here console.log(`Hello ${name} length: ${name.length}`); return name.length; }به علاوه : به منظور تولید مستندات پروژه Angular خود می توانید از این ابزار Compodoc استفاده کنید.8. از Async Pipe برای Observablesها استفاده کنید.چه : از async pipe برای subscribe کردن به observableهای موجود در templatesها استفاده کنید.چرا : subscriptionها را به صورت خودکار مدیریت می کند و با از بین رفتن کامپوننت اتصال را از بین میبرد و از نشت حافظه(memory leak) جلوگیری می کند.چگونه :// In component data$: Observable<Data>;<!-- In template --> <div *ngIf=&quotdata$ | async as data&quot>{{ data }}</div>9. راهنمای Angular Style را دنبال کنید.چه چیزی : برای استانداردهای کدنویسی ، به راهنمای رسمی Angular style پایبند باشید.چرا : سازگاری خوانایی و قابلیت نگهداری را بهبود می بخشد.چگونه :// Bad function myComponent() {} // Good export class MyComponent {}10. از سرویس های Angular برای نوشتن منطق یا logic استفاده کنید.چه : business logic را در سرویس های Angular قرار دهید، نه کامپوننت ها.چرا : جداسازی بخش ها و قابلیت استفاده مجدد از منطق.چگونه :@Injectable({ providedIn: 'root', }) export class MyService { getData(): Observable<Data> { // Write Service logic (business logic) here } }@Component({ selector: 'app-my-component', templateUrl: 'my-component.component.html', }) export class MyComponent { currentValue: Data; constructor(private myService: MyService) { myService.getData().subscribe(v => { this.currentValue = v; }); } }11. از Lifecycle Hooks استفاده کنید.چه : انگولار lifecycle hook را برای مدیریت رفتار کامپوننت ها در لحظات کلیدی فراهم می کند. از متد هایی مانند ngOnInit، ngOnChangesو ngOnDestroyو غیره به درستی استفاده کنید. چرا : استفاده صحیح از متدهای initialization ، lifecycle hook کارآمد کامپوننت ها، اجرای بهتر change detection و پاکسازی بهتر کامپوننت ها را تضمین می کند.چگونه :import { Component, OnInit, OnDestroy } from '@angular/core'; @Component({ selector: 'app-example', template: '...', }) export class ExampleComponent implements OnInit, OnDestroy { constructor(){} ngOnInit() { // Initialization logic here } ngOnDestroy() { // Cleanup logic here } }12. الگوهای مناسب Subscription/Unsubscription را دنبال کنید.چه : از memory leak با unsubscribe کردن observableها در هنگام از بین رفتن کامپوننت ها جلوگیری کنید.چرا : با unsubscribe کردن هنگام تخریب کامپوننت ها از subscribeهای طولانی مدت و بی استفاده جلوگیری می کنید، که می تواند منجر به نشت حافظه و رفتار غیرمنتظره شود.چگونه :import { Component, OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-example', template: '...', }) export class ExampleComponent implements OnInit, OnDestroy { private subscription: Subscription; constructor(private dataService: DataService) { } ngOnInit() { this.subscription = this.dataService.getData().subscribe(data => { // Handle data }); } ngOnDestroy() { this.subscription.unsubscribe(); } }13. بجای نوع «any» از interfaceها استفاده کنید.چه : از interface ها برای تعریف نوع اشیا به جای استفاده از نوع "any" استفاده کنید.چرا : interface ها ایمنی نوع داده ها و خوانایی بهتر کد را فراهم می کنند. هنگام نوشتن با IDE ترجیحی خود (VSCode، IntelliJ Idea یا موارد دیگر)، در طول توسعه راهنمایی می شویم. علاوه بر این ما از Angular استفاده می کنیم که بر اساس Typescript است که از برنامه نویسی شی گرا پشتیبانی می کند. OOP راهی برای سازماندهی کد و ایجاد برنامه های نرم افزاری قابل استفاده مجدد و ماژولار ارائه می دهد. برای انجام این کار، وراثت، encapsulation و polymorphism وجود دارند، اما اول از همه ما انواع داده ها را داریم. پس چرا به جای استفاده از "any" به درستی از آنها استفاده نمی کنیم؟چگونه :interface User { id: number; name: string; } function getUserInfo(user: User): string { // Access user properties safely return user.name + &quot (&quot + user.id + &quot)&quot }14. سازگاری و استایل کدچه چیزی : یک استایل کدنویسی ثابت را دنبال کنید و به راهنمای استایل Angular پایبند باشید.چرا : کد یکنواخت خوانایی و همکاری بین توسعه دهندگان را افزایش می دهد.چگونه :برای اجرای استانداردهای کدنویسی از یک linter مانند ESLint یا TSLint استفاده کنید.راهنمای استایل Angular را دنبال کنید: https://angular.io/guide/styleguide15. تست واحد (Unit test) و تست e2eچه : تست های واحد را با استفاده از ابزارهایی مانند Jasmine و Karma بنویسید. با استفاده از یک ابزار خاص برای e2e مانند Cypress، تست end-to-end انجام دهید.چرا : تست قابلیت اطمینان کد را تضمین می کند، اشکالات را زودتر تشخیص می دهد و تغییرات کدهای آینده را تسهیل می کند.چگونه (Unit test):// example.component.spec.ts// example.e2e-spec.ts describe('Example App', () => { it('should display welcome message', () => { cy.visit('/'); cy.get('app-root').contains('Welcome to your Angular App!'); }); }); describe('ExampleComponent', () => { it('should create', () => { const component = new ExampleComponent(); expect(component).toBeTruthy(); }); });چگونه (تست e2e):// example.e2e-spec.ts describe('Example App', () => { it('should display welcome message', () => { cy.visit('/'); cy.get('app-root').contains('Welcome to your Angular App!'); }); });16. از عملگرهای RxJS به طور موثر استفاده کنید.چه : عملگرهای RxJS مانند map, filter, و mergeMapتبدیل و دستکاری observable را به طور موثر انجام می دهند.چرا : عملگرها عملیات پیچیده async را ساده می کنند و کد را مختصر و خوانا می کنند.چگونه :import { from } from 'rxjs'; import { map, filter } from 'rxjs/operators'; const numbers = from([1, 2, 3, 4, 5]); numbers.pipe( filter(num => num % 2 === 0), // Filters even numbers map(num => num * 2) // Doubles the filtered numbers ).subscribe(result => { console.log(result); // Output: 4, 8 });import { Observable } from 'rxjs'; import { map, filter } from 'rxjs/operators'; // Example using map and filter operators observable.pipe( filter(data => data !== null), // Filter out null values map(data => data.property), // Extract a specific property ).subscribe(transformedData => { // Process transformed data });17: از subscribeهای تودرتو خودداری کنید.چه : از subscribe کردن درون یک subscribe دیگر برای جلوگیری از callback hell و بهبود خوانایی کد خودداری کنید.چرا : subscribeهای تودرتو می‌توانند به کدهایی منجر شوند که نگهداری آن دشوار است، که به عنوان pyramid of doom «هرم عذاب» شناخته می‌شود. همچنین در صورت عدم مدیریت صحیح می تواند باعث نشت حافظه شود.چگونه :import { Observable } from 'rxjs'; import { switchMap } from 'rxjs/operators'; // Bad Practice (Nested Subscriptions) observable1.subscribe(data1 => { observable2.subscribe(data2 => { // Process data }); }); // Good Practice (Using switchMap) observable1.pipe( switchMap(data1 => { return observable2; }) ).subscribe(data2 => { // Process data });18: استفاده مجدد از کامپوننت هاچه : کامپوننت های قابل استفاده مجدد را برای کپسوله کردن عملکردها و توابع خاص و عناصر UI مربوط به آنها ایجاد کنید.چرا : استفاده مجدد از کامپوننت ها قابلیت نگهداری را افزایش می دهد، تکرار را کاهش می دهد و ثبات کلی برنامه را افزایش می دهد.چگونه :// Reusable Component (Example) @Component({ selector: 'app-custom-button', template: '<button>{{label}}</button>' }) export class CustomButtonComponent { @Input() label: string; // Component logic and methods } // Implementation in Parent Component <app-custom-button [label]=&quot'Click me'&quot></app-custom-button>19: از عملگرهای Mapping با مرتبه بالاتر استفاده کنید.چه : از عملگرهای mapping مرتبه بالاتر مانند mergeMap، concatMapیا exhaustMapبر اساس نیاز استفاده کنید.چرا : عملگرهای mapping درجه بالاتر به شما امکان می دهند همزمانی را مدیریت کنید و انتشار observable را به شیوه ای کنترل شده مدیریت کنید و از مسائلی مانند race جلوگیری کنید.چگونه :import { Observable, from } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; // Using mergeMap to handle concurrent requests observable.pipe( mergeMap(data => { return from(someAsyncOperation(data)); }) ).subscribe(result => { // Process the merged result });20: از TakeUntil با Subject برای unsubscribe دستی استفاده کنید.چه:در صورت امکان از پایپ async استفاده کنید (این توسط Angular پیشنهاد شده است). هنگامی که نمی توانید از async pipe استفاده کنید، از عملگرtakeUntil به همراه Subjectبرایunsubscribe دستی از observableها استفاده کنید.چرا:عملگر takeUntilبه شما این امکان را می دهد تا در صورت وقوع یک رویداد خاص (که توسط Subject کنترل می شود) از observableها unsubscribe کنید و از نشت حافظه جلوگیری کنید.چگونه :import { Component, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-example', template: 'Example Component' }) export class ExampleComponent implements OnDestroy { private unsubscribe$: Subject<void> = new Subject<void>(); constructor(private dataService: DataService) { this.dataService.getData() .pipe(takeUntil(this.unsubscribe$)) .subscribe(data => { // Handle data }); } ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); } }با پیروی از این best practiceها ، توسعه‌دهندگان Angular می‌توانند کدی کارآمدتر، با خوانایی و قابلیت نگهداری بیشتر را ایجاد کنند و در عین حال از ویژگی‌های قدرتمند ارائه‌شده توسطAngular و RxJS بهره‌مند شوند .این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.