Xây dựng 1 ứng dụng Angular___BestPractice1

LEVEL UP!

Clean và Performant?

Trong năm qua 2018, nhóm của chúng tôi đang/đã tinh chỉnh ứng dụng cả về các tiêu chuẩn và hiệu suất mã hóa để làm cho nó ở trạng thái tốt nhất có thể. Bài viết này phác thảo quá trình thực hành của chúng tôi.

Nó có liên quan đến Angular, Typescript, RxJs @ngrx/store. Chúng tôi cũng sẽ đi qua một số nguyên tắc mã hóa chung (rules render) để giúp cho ứng dụng của bạn trong tương lai trở nên sạch & hoạt động chỉn chu hơn.

1) Sử dụng trackBy

Khi sử dụng ngFor để lặp qua một mảng(array) trong các khuôn mẫu(ng-template), hãy sử dụng nó với một hàm trackBy sẽ trả về một mã định danh duy nhất(unique identifier) cho mỗi mục(item).

Why?

Khi một mảng (array) thay đổi, Angular sẽ render lại (re-renders) toàn bộ cây DOM. Nhưng nếu bạn sử dụng trackBy, Angular sẽ biết phần tử(element) nào đang/đã được thay đổi và sẽ chỉ thực hiện thay đổi DOM cho phần tử cụ thể đó. Để có giải thích thêm về điều này có thể xem tại đây: @NetanelBasal

Before:

<li *ngFor="let item of items;">{{ item }}</li>

After:

// in the template
<li *ngFor="let item of items; trackBy: trackByFn">{{ item }}</li>
// in the component
trackByFn(index, item) {    
   return item.id; // unique id corresponding to the item
}

2) Sử dụng const vs let

Khi khai báo biến, hãy sử dụng const khi giá trị không được gán lại (reassigned).

Why?

  • Sử dụnglet &&const ở những trường hợp thích hợp nhất sẽ giúp cho việc khai báo các biến rõ ràng hơn.

  • Nó cũng sẽ giúp xác định các vấn đề(Intifying issues) khi một giá trị được gán lại vô tình bằng cách ném một lỗi khi ta biên dịch (throwing a compile time error).

  • Nó cũng giúp cải thiện(improve) khả năng đọc mã (readability of the code).

Before:

let car = 'ludicrous car';
let myCar = `My ${car}`;
let yourCar = `Your ${car};
if (iHaveMoreThanOneCar) {
   myCar = `${myCar}s`;
}
if (youHaveMoreThanOneCar) {
   yourCar = `${youCar}s`;
}

After:

// the value of car is not reassigned, so we can make it a const
const car = 'ludicrous car';
let myCar = `My ${car}`;
let yourCar = `Your ${car};
if (iHaveMoreThanOneCar) {
   myCar = `${myCar}s`;
}
if (youHaveMoreThanOneCar) {
   yourCar = `${youCar}s`;
}

3) Sử dụng Pipeable operators

Sử dụng pipeable operators khi sử dụng RxJs operators.

Why?

Các toán tử Pipeable là cây có thể thay đổi được (tree-shakeable) nghĩa là chỉ có mã chúng ta cần thực thi sẽ được đưa vào khi chúng đã được Imported.

Điều này cũng giúp dễ dàng xác định các toán tử nào không sử dụng (identify unused operators) trong các tệp (**.ts).

Chú ý: Điều này hoạt động trên phiên bản Angular version 5.5+.

Before:

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
iAmAnObservable
    .map(value => value.item)
    .take(1);

After:

import { map, take } from 'rxjs/operators';
iAmAnObservable
    .pipe(
       map(value => value.item),
       take(1)
     );

4) Isolate API hacks (Hash code để fix issue cho API) :)) có thể hiểu là vậy

Không phải tất cả API đều trả về kết quả ngay ở component khi được gọi (bullet proof) — đôi khi chúng ta cần thêm một số logic vào mã để bù đắp lỗi trong các API s. Thay vì gọi api trong các components cần thiết, tốt hơn là cô lập chúng ở một nơi - như trong một service và sử dụng service từ component.

Why?

Khi sửa lỗi trong các API, việc tìm kiếm chúng dễ dàng hơn trong một tệp thay vì phải tìm kiếm ở trong các components có thể được trải rộng ở toàn bộ code.

Bạn cũng có thể tạo các thẻ tùy chỉnh như API_FIX tương tự như TODO và gắn thẻ các bản sửa lỗi với thẻ để dễ tìm hơn.

Before:

// component
export class AppComponent {
  data$;
  contructor(private http: HttpClient) {}
  
  this.data$ = this.http.get('/api/todo')
    .pipe(
       map(value => value.items),
       catchError(err => of(err))
     );  
}

After:

// component
export class AppComponent {
  data$;
  contructor(private _todoservice: TodoService) {}
  
  this.data$ = _todoservice.getAll()
    .pipe(
       map(value => value.items)
     );  
}

5) Subscribe in template

Tránh đăng ký các subscribing to observables từ các component và thay vào đó hãy đăng ký các subscribing to observables từ template

Why?

async pipe tự hủy đăng ký tự động(unsubscribe themselves automatically) và nó làm cho mã đơn giản hơn bằng cách loại bỏ sự cần thiết phải quản lý subscriptions theo cách thủ công. Nó cũng làm giảm nguy cơ vô tình quên để hủy đăng ký unsubscribe của một subscriptions trong component, mà sẽ gây ra một rò rỉ bộ nhớ(memory leak). Nguy cơ này cũng có thể được giảm thiểu bằng cách sử dụng quy tắc lint-rule để phát hiện các unsubscribed chưa được hủy.

Điều này cũng ngăn các component khỏi trạng thái trạng thái và các lỗi của dữ liệu bị đột biến bên ngoài subscriptions.

Before:

// template
<p>{{ textToDisplay }}</p>
// component
iAmAnObservable
    .pipe(
       map(value => value.item),
       takeUntil(this._destroyed$)
     )
    .subscribe(item => this.textToDisplay = item);

After:

// template
<p>{{ textToDisplay$ | async }}</p>
// component
this.textToDisplay$ = iAmAnObservable
    .pipe(
       map(value => value.item)
     );

6) Clean up subscriptions

Khi subscribing to observables, luôn đảm bảo bạn đã unsubscribe chúng một cách thích hợp bằng cách sử dụng toán tử như: take, takeUntil, v.v.

Why?

Before:

After:

7) Use appropriate operators

Why?

Before:

After:

8) Lazy load

Why?

Before:

After:

9) Avoid having subscriptions inside subscriptions

Why?

Before:

After:

10) Avoid any; type everything;

Why?

Before:

After:

11) Make use of lint rules

Why?

Before:

After:

13) Small reusable components

Why?

Before:

After:

14) Components should only deal with display logic

Why?

Before:

After:

15) Avoid long methods

Why?

Before:

After:

16) DRY

Why?

Before:

After:

17) Add caching mechanisms

Why?

Before:

After:

18) Avoid logic in templates

19) Strings should be safe

Bigger picture

Xem thêm tại đây: best practices for a clean and performant angular application.

Last updated