지난주에 RxJS 질문에 답변 했는데 다른 커뮤니티 멤버와 “모든 특정 부작용에 대해 구독을 작성해야합니까? 아니면 일반적으로 구독을 최소화해야합니까?” 완전한 반응 형 응용 프로그램 접근 방식 또는 서로 전환하는 시점에서 사용할 방법을 알고 싶습니다. 이것은 저와 다른 사람들이 불쾌한 토론을 피하는 데 도움이 될 것입니다.
설치 정보
- 모든 예제는 TypeScript에 있습니다.
- 질문에 더 초점을 맞추려면 구독에 수명주기 / 생성자를 사용하지 않고 프레임 워크를 관련이 없습니다.
- 상상해보십시오. 구독은 생성자 / 라이프 사이클 초기화에 추가됩니다.
- 상상 : 구독 취소는 수명주기에서 완료됩니다.
부작용이란 무엇입니까 (각도 샘플)
- 업데이트 / UI에 입력 (예
value$ | async
) - 구성 요소의 출력 / 업스트림 (예
@Output event = event$
) - 서로 다른 계층에서 서로 다른 서비스 간 상호 작용
예시적인 사용 사례 :
- 두 가지 기능 :
foo: () => void; bar: (arg: any) => void
- 두 가지 관측소 :
http$: Observable<any>; click$: Observable<void>
foo
http$
방출 후 호출 되며 값이 필요하지 않습니다.bar
click$
방출 후 호출 되지만 현재 값이 필요합니다.http$
사례 : 모든 특정 부작용에 대한 구독 생성
const foo$ = http$.pipe(
mapTo(void 0)
);
const bar$ = http$.pipe(
switchMap(httpValue => click$.pipe(
mapTo(httpValue)
)
);
foo$.subscribe(foo);
bar$.subscribe(bar);
사례 : 일반적으로 구독 최소화
http$.pipe(
tap(() => foo()),
switchMap(httpValue => click$.pipe(
mapTo(httpValue )
)
).subscribe(bar);
간단히 말해서 내 자신의 의견
구독자가 Rx 환경을 더 복잡하게 만든다는 사실을 이해할 수 있습니다. 가입자가 파이프에 영향을 미치거나 예를 들어 관찰 할 수 있는지 여부를 고려해야하기 때문입니다. 그러나 코드를 더 많이 분리할수록 (포커스가 많을수록 발생하는시기) 나중에 코드를 유지 관리 (테스트, 디버그, 업데이트)하기가 쉬워집니다. 이를 염두에두고 항상 관찰 가능한 단일 소스와 코드의 부작용에 대한 단일 구독을 만듭니다. 두 개 이상의 부작용이 정확히 동일한 소스 관찰 가능 항목에 의해 트리거되는 경우 관찰 가능 항목을 공유하고 각 부작용을 개별적으로 구독합니다. 수명주기가 다를 수 있기 때문입니다.
답변
RxJS는 비동기 작업을 관리하는 데 유용한 리소스이며 가능한 경우 코드 (구독 수 감소 포함)를 단순화하는 데 사용해야합니다. 마찬가지로 RxJS가 애플리케이션에서 전체 구독 수를 줄일 수있는 솔루션을 제공하는 경우 옵저버 블 뒤에 자동으로 해당 옵저버 블에 대한 구독이 없어야합니다.
그러나 ‘필수’가 아닌 구독을 작성하는 것이 유리한 상황이 있습니다.
예외 예-단일 템플릿에서 관찰 가능 항목 재사용
첫 번째 예를 보면 :
// Component:
this.value$ = this.store$.pipe(select(selectValue));
// Template:
<div>{{value$ | async}}</div>
value $가 템플릿에서 한 번만 사용되는 경우 비동기 파이프와 코드 절약 및 자동 가입 취소에 대한 이점을 활용합니다. 그러나이 답변 에 따라 템플릿의 동일한 비동기 변수에 대한 여러 참조는 피해야합니다.
// It works, but don't do this...
<ul *ngIf="value$ | async">
<li *ngFor="let val of value$ | async">{{val}}</li>
</ul>
이 상황에서 대신 별도의 구독을 작성하고이를 사용하여 컴포넌트에서 비동기가 아닌 변수를 업데이트합니다.
// Component
valueSub: Subscription;
value: number[];
ngOnInit() {
this.valueSub = this.store$.pipe(select(selectValue)).subscribe(response => this.value = response);
}
ngOnDestroy() {
this.valueSub.unsubscribe();
}
// Template
<ul *ngIf="value">
<li *ngFor="let val of value">{{val}}</li>
</ul>
기술적으로는 없이도 동일한 결과를 얻을 수 valueSub
있지만 응용 프로그램의 요구 사항은 이것이 올바른 선택임을 의미합니다.
구독 여부를 결정하기 전에 관찰 가능 항목의 역할 및 수명 고려
둘 이상의 관측 가능 항목을 함께 사용할 때만 사용하는 경우 적절한 RxJS 연산자를 사용하여 단일 관측 값으로 결합해야합니다.
마찬가지로, first () 를 사용하여 Observable의 첫 번째 방출을 제외한 모든 것을 걸러내는 경우, 지속적인 역할을 수행하는 Observable보다 코드를 경제적으로 사용하고 ‘추가’구독을 피해야 할 더 큰 이유가 있다고 생각합니다. 세션.
개별 옵저버 블 중 하나가 다른 옵저버 블과 독립적으로 유용한 경우 별도의 구독 (들)의 유연성과 명확성을 고려할 가치가 있습니다. 그러나 초기 진술에 따라 명확한 이유가없는 한 모든 관찰 가능 항목에 대해 구독을 자동으로 생성해서는 안됩니다.
수신 거부 관련 :
추가 구독 에 대한 요점 은 더 많은 구독 취소 가 필요하다는 것입니다. 말씀 드린대로, 필요한 모든 탈퇴가 Destroy에 적용되었다고 가정하고 싶지만 실제 생활이 항상 그렇게 원활하게 진행되는 것은 아닙니다! 다시, RxJS는 이 프로세스를 간소화 하는 유용한 도구 (예 : first () )를 제공하여 코드를 단순화하고 메모리 누수 가능성을 줄입니다. 이 기사 에서는 유용한 추가 정보와 예제를 제공합니다.
개인 취향 / 상세와 간결함 :
자신의 취향을 고려하십시오. 코드의 세부 정보에 대한 일반적인 토론을하고 싶지는 않지만 너무 많은 ‘노이즈’와 코드를 지나치게 암호로 만드는 것 사이의 적절한 균형을 찾는 것이 목표입니다. 이것은 가치가있을 수 있습니다 .
답변
구독 최적화가 최종 게임이라면 논리적 인 극단으로 가서 다음과 같은 일반적인 패턴을 따르십시오.
const obs1$ = src1$.pipe(tap(effect1))
const obs2$ = src2$pipe(tap(effect2))
merge(obs1$, obs2$).subscribe()
탭에서 독점적으로 부작용을 실행하고 병합을 사용하여 활성화하면 구독이 하나만 있음을 의미합니다.
이것을하지 않는 한 가지 이유는 RxJS를 유용하게 만드는 많은 것들을 중립화하고 있기 때문입니다. 관찰 가능한 스트림을 작성하고 필요에 따라 스트림을 구독 / 구독 해제하는 기능입니다.
나는 관찰 가능 항목이 논리적으로 구성되어 있고 구독 감소라는 이름으로 오염되거나 혼동되어서는 안된다고 주장합니다. foo 효과를 논리적으로 bar 효과와 결합해야합니까? 하나는 다른 하나를 필요로합니까? http $가 발생할 때 foo 트리거를 원하지 않을 것입니까? 관련없는 기능간에 불필요한 연결을 생성하고 있습니까? 이것들은 모두 하나의 스트림에 넣지 않는 이유입니다.
이것은 여러 구독 IMO로 관리하기 쉬운 오류 처리를 고려하지 않아도됩니다.