앵귤러 스타일 가이드

목차

  1. 파일을 400 줄로 제한하는 것을 고려하십시오.
  2. 함수를 짧게 작성하세요.
  3. trackBy
  4. const vs let
  5. Pipeable operators
  6. Isolate API hacks
  7. Subscribe in template
  8. Clean up subscriptions
  9. Use appropriate operators
  10. Lazy load
  11. Avoid having subscriptions inside subscriptions
  12. Avoid any type everything
  13. Make use of lint rules
  14. Small reusable components
  15. Components should only deal with display logic
  16. Avoid long methods
  17. DRY
  18. Add caching mechanisms
  19. Avoid logic in templates
  20. Strings should be safe
  21. State Management
  22. Immutable state
  23. Jest
  24. Karma
  25. Universal

참조 URL

파일을 400 줄로 제한하는 것을 고려하십시오.

top

함수를 짧게 작성하세요.

top

trackBy

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

// good
// in the template

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

top

const vs let

// bad
let car = 'ludicrous car';
let myCar =`My $ {car}`;

// good
const car = 'ludicrous car';
let myCar =`My $ {car}`;
yourCar =`Your $ {car};

top

Pipeable operators

// bad
import "rxjs/add/operator/map";
import "rxjs/add/operator/take";

iAmAnObservable.map(value => value.item).take(1);

// good
import { map, take } from "rxjs/operators";

iAmAnObservable.pipe(
  map(value => value.item),
  take(1)
);

top

Isolate API hacks

top

Subscribe in template

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

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

top

Clean up subscriptions

// bad
iAmAnObservable
.pipe(
  map(value => value.item)
)
.subscribe(item => this.textToDisplay = item);

// good
private destroyed$ = new Subject();
public ngOnInit (): void {
  iAmAnObservable
  .pipe(
    map(value => value.item)
    // We want to listen to iAmAnObservable until the component is destroyed,
    takeUntil(this._destroyed$)
  )
  .subscribe(item => this.textToDisplay = item);
}

public ngOnDestroy (): void {
  this._destroyed$.next();
}
// good
iAmAnObservable
.pipe (
  map (value => value.item),
  take (1),
  takeUntil (this._destroyed$)
)
.subscribe (item => this.textToDisplay = item);

top

Use appropriate operators

top

Lazy load

// bad
// app.routing.ts
{ path: 'not-lazy-loaded', component: NotLazyLoadedComponent }

// good
// app.routing.ts
{
  path: 'lazy-load',
  loadChildren: 'lazy-load.module#LazyLoadModule'
}
// lazy-load.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { LazyLoadComponent }   from './lazy-load.component';
@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([
      {
        path: '',
        component: LazyLoadComponent
      }
    ])
  ],
  declarations: [
    LazyLoadComponent
  ]
})

export class LazyModule {}

top

Avoid having subscriptions inside subscriptions

// bad
firstbuservable$.pipe (
  take (1)
)
.subscribe(firstValue => {
  secondObservable$.pipe (
    take(1)
  )
  .subscribe(secondValue => {
    console.log (`결합 된 값은 $ {firstValue}와 $ { secondValue}`);
  });
});

// good
firstObservable$.pipe (
  withLatestFrom(secondObservable$),
  first()
)
.subscribe(([firstValue, secondValue]) => {
  console.log(`Combined values are: ${firstValue} & ${secondValue}`);
});

top

Avoid any; type everything;

// bad
const x = 1;
const y = 'a';
const z = x + y;
console.log(`Value of z is: ${z}`);
// Output
Value of z is 1a

// good
const x: number = 1;
const y: number = 'a';
const z: number = x + y;
// This will give a compile error saying:
Type '"a"' is not assignable to type 'number'.
const y:number

top

Make use of lint rules

// bad
public ngOnInit (): void {
  console.log('I am a naughty console log message');
  console.warn('I am a naughty console warning message');
  console.error('I am a naughty console error message');
}
// Output
 No errors, prints the below on console window:
I am a naughty console message
I am a naughty console warning message
I am a naughty console error message

// good
// tslint.json
{
  "rules": {
    .......
    "no-console": [
      true,
      "log",    // no console.log allowed
      "warn"    // no console.warn allowed
    ]
  }
}
// ..component.ts
public ngOnInit (): void {
  console.log('I am a naughty console log message');
  console.warn('I am a naughty console warning message');
  console.error('I am a naughty console error message');
}
// Output
Lint errors for console.log and console.warn statements and no error for console.error as it is not mentioned in the config
Calls to 'console.log' are not allowed.
Calls to 'console.warn' are not allowed.

top

Small reusable components

top

Components should only deal with display logic

top

Avoid long methods

top

DRY

top

Add caching mechanisms

top

Avoid logic in templates

// bad
// template
<p *ngIf="role==='developer'"> Status: Developer </p>
// component
public ngOnInit (): void {
  this.role = 'developer';
}

// good
// template
<p *ngIf="showDeveloperStatus"> Status: Developer </p>
// component
public ngOnInit (): void {
  this.role = 'developer';
  this.showDeveloperStatus = true;
}

top

Strings should be safe

// bad
private myStringValue: string;
if (itShouldHaveFirstValue) {
  myStringValue = 'First';
  } else {
    myStringValue = 'Second'
  }

// good
private myStringValue: 'First' | 'Second';
if (itShouldHaveFirstValue) {
  myStringValue = 'First';
} else {
  myStringValue = 'Other'
}
// This will give the below error
Type '"Other"' is not assignable to type '"First" | "Second"'
(property) AppComponent.myValue: "First" | "Second"

top

State Management

Consider using @ngrx/store for maintaining the state of your application and @ngrx/effects as the side effect model for store. State changes are described by the actions and the changes are done by pure functions called reducers.

Why?

@ngrx/store isolates all state related logic in one place and makes it consistent across the application. It also has memoization mechanism in place when accessing the information in the store leading to a more performant application. @ngrx/store combined with the change detection strategy of Angular leads to a faster application.

top

Immutable state

When using @ngrx/store, consider using ngrx-store-freeze to make the state immutable. ngrx-store-freeze prevents the state from being mutated by throwing an exception. This avoids accidental mutation of the state leading to unwanted consequences.

Why?

Mutating state in components leads to the app behaving inconsistently depending on the order components are loaded. It breaks the mental model of the redux pattern. Changes can end up overridden if the store state changes and re-emits. Separation of concerns — components are view layer, they should not know how to change state.

top

Jest

top

Karma

top

Universal

top

Conclusion

좋은 가이드라인을 잡아 주실분은 댓글이나 커밋해주시면 잘 배우겠습니다.

참조

top