rx 조회를 각도 항목에 추가하는 장점

34140 단어 stateangularrxjs
트위터에서 저를 팔로우해 구독Newsletter이 최초로 timdeschryver.dev에 발표되었습니다.
이 글에서 우리는 영웅의 각도 여행에 rx-query 을 추가하고 rx-query의 장점을 지적할 것이다.시작하기 전에 rx-query의 영감은 react-querySWR에서 왔다.rx-query는 HTTP를 통해 데이터를 얻을 수 있는 간단한 방법을 제공합니다.
각도 응용에 있어서 상황은 이미 이렇다. 그렇다면 당신은 왜 신경을 써야 합니까?
추가 rx-query 는 별도의 작업이 필요하지 않기 때문에, 당신은 무료로 유용한 기능을 얻을 수 있습니다.

질의 상태

rx-query 를 사용하여 HTTP 요청을 보내는 것은 일반 요청과 마찬가지로 간단합니다. query 방법으로 요청을 포장하고 조회 키를 제공하기만 하면 됩니다.관건은 여러 개의 조회를 구분하는 것이다. 다음 장에서 이 점은 매우 중요하다.
import { query } from 'rx-query'

export class HeroesComponent {
  heroes$ = query('heroes-list', () => this.heroService.getHeroes())

  constructor(private heroService: HeroService) {}
}
일반 요청과 마찬가지로 query 방법은 관찰할 수 있는 Observable<QueryOutput<T>> 방법을 되돌려줍니다.관찰 가능 객체는 요청된 각 단계별 값 success, error, loading, refreshing, status 입니다.이것은 출력상의 ngSwitch 속성을 공개하고 상태와 heroes-list directive를 결합하여 사용하면 요청한 단계마다 다른 보기를 표시하기 쉽다.
서로 다른 단계를 위해 보기를 만들 필요는 없지만 개발자가 불쾌한 경로를 주의해야 한다.
더욱 좋은 사용자 체험을 가져오다.
<ng-container *ngIf="heroes$ | async as query">
  <ng-container [ngSwitch]="query.status">
    <ul class="heroes" *ngSwitchDefault>
      <li *ngFor="let hero of query.data">
        <a routerLink="/detail/{{ hero.id }}">
          <span class="badge">{{ hero.id }}</span> {{ hero.name }}
        </a>
      </li>
    </ul>

    <div *ngSwitchCase="'loading'">Loading ...</div>
    <div *ngSwitchCase="'error'">Error ({{query.error}})</div>
  </ng-container>
</ng-container>
결과는 다음과 같습니다.

조회는 입력 흐름이 있을 수 있다

query 조회는 입력할 필요가 없습니다. 왜냐하면 그것은 전체 영웅 목록을 얻기 때문입니다.
매개 변수를 입력해야 하는 조회에 rx-query 에 다시 불러올 수 있습니다. 정적 매개 변수나 관찰 가능한 매개 변수를 전달할 수 있습니다.
이것은 우리로 하여금 개발자로서 사용하기 쉽게 한다.
관찰 가능한 대상을 입력으로 사용할 때, 관찰 가능한 대상이 새로운 값을 보낼 때, 조회 리셋을 호출하고, (포장되지 않은) 값을 사용합니다.
이것은 루트 매개 변수에 따라 데이터를 가져와야 하는 구성 요소에 매우 유용합니다. 예를 들어hero의 상세한 정보 페이지입니다.

NOTE: For these queries, the input will be appended (by using the JSON.stringify method) to the key to generate a unique key.


export class HeroDetailComponent {
  // Static parameter
  // Generates the key "hero-5" when the id is 5
  hero$ = query('hero', +this.route.snapshot.paramMap.get('id'), (id) =>
    this.heroService.getHero(id),
  )

  // Observable parameter
  // Generates the key when it receives a new input value
  hero$ = query('hero', this.route.params.pipe(map((p) => +p.id)), (id) =>
    this.heroService.getHero(id),
  )

  constructor(
    private route: ActivatedRoute,
    private heroService: HeroService,
  ) {}
}

캐시 쿼리


쿼리 키를 제공해야 하는 이유는 prefetch 쿼리를 캐시할 수 있기 때문입니다.
캐시 레이어에는 다음과 같은 세 가지 이점이 있습니다.
  • 백그라운드에서 데이터를 갱신할 때 캐시 데이터를 되돌려줍니다.
  • 상태가 여전히'신선'으로 간주될 때 과도한 캡처를 무시할 수 있다.
  • 같은 요청이 끊겼을 때 같은 키에 대한 전송 요청을 무시합니다.
  • 검색은 캐시이기 때문에 프로그램이 기본 동작보다 더 빠른 것 같습니다.
    이것은 다음 두 GIF에서 볼 수 있습니다.


    프리페치


    우리는 사용자가 내비게이션 후에 기다릴 필요가 없도록 이 캐시를 사용하여 데이터를 추출할 수 있다.query 방법과 prefetch 방법은 같은 서명을 가지고 있으나 결과를 되돌려 주지 않는다.
    prefetch('hero', heroId, () => this.heroService.getHero(heroId))
    
    만약 우리가 다시 사용할 수 있는 error 지령 (예를 들어 아래의 지령) 을 만들면 데이터를 추출하는 것이 매우 쉬워질 것이다.
    @Directive({
      selector: '[prefetch]',
    })
    export class PrefetchDirective implements OnInit, AfterViewInit, OnDestroy {
      @Input()
      prefetchMode: ('load' | 'hover' | 'visible')[] = ['visible']
      @Output()
      prefetch = new EventEmitter<void>()
    
      observer: IntersectionObserver
      loaded = false
    
      constructor(private elemRef: ElementRef) {}
    
      ngOnInit() {
        if (this.prefetchMode.includes('load')) {
          this.prefetchData()
        }
      }
    
      ngAfterViewInit() {
        this.observer = new IntersectionObserver((entries) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              this.prefetchData()
              this.observer.disconnect()
            }
          })
        })
        this.observer.observe(this.elemRef.nativeElement)
      }
    
      ngOnDestroy() {
        if (this.observer) {
          this.observer.disconnect()
        }
      }
    
      @HostListener('mouseenter')
      onMouseEnter() {
        if (!this.loaded && this.prefetchMode.includes('hover')) {
          this.loaded = true
          this.prefetchData()
        }
      }
    
      prefetchData() {
        if (navigator.connection.saveData) {
          return undefined
        }
        this.prefetch.next()
      }
    }
    
    그리고 우리는 다음과 같은 방식으로 영웅의 상세한 정보를 추출할 수 있다.
    @Component({
      selector: 'app-heroes',
      template: `
        <ng-container *ngIf="heroes$ | async as query">
          <ng-container [ngSwitch]="query.status">
            <ul class="heroes" *ngSwitchDefault>
              <li *ngFor="let hero of query.data" (prefetch)="prefetch(hero.id)">
                <a routerLink="/detail/{{ hero.id }}">
                  <span class="badge">{{ hero.id }}</span> {{ hero.name }}
                </a>
              </li>
            </ul>
    
            <div *ngSwitchCase="'loading'">Loading ...</div>
            <div *ngSwitchCase="'error'">Error ... ({{ query.error }})</div>
          </ng-container>
        </ng-container>
      `,
    })
    export class HeroesComponent {
      heroes$ = query('heroes-list', () => this.heroService.getHeroes())
    
      constructor(private heroService: HeroService) {}
    
      prefetch(heroId: number) {
        prefetch('hero', heroId, () => this.heroService.getHero(heroId))
      }
    }
    
    이제 사용자가 디테일 보기로 이동할 때 디테일이 바로 표시됩니다.

    There are other solutions to achieve the same result, for example with NgRx, which I wrote about in Making your application feel faster by prefetching data with NgRx.


    질의 재시도


    때때로 서버가 시간을 초과하거나 서버가 불량 상태이기 때문에 요청이 실패할 수 있습니다.
    쿼리가 최종적으로 loading 상태가 되기 전에 쿼리를 세 번 다시 시도하여 성공적인 응답을 받도록 합니다.
    이러한 방법으로 인해 사용자 체험이 개선되었다.
    캐시에 데이터가 있으면 재시도를 기다리는 동안 이 데이터를 사용합니다.
    동작은 이와 같습니다. 데이터가 없을 때 조회는 최대 재시도 횟수에 도달할 때까지 rx-query 상태를 유지합니다.

    질의 새로 고침


    클라이언트 사이트에 저장된 상태가 유행이 지났습니다.이것이 바로 rx-query 상태를 갱신하기 위해 여러 가지 옵션을 제공한 이유입니다.
    x밀리초 후에 다시 추출하는 것 외에 창에서 초점을 받았을 때 다시 추출하는 요청도 설정할 수 있습니다.
    이것은 사용자가 항상 새로운 상태를 사용하도록 보장할 수 있다.
    export class DashboardComponent {
      heroes$ = query(
        'heroes-dashboard',
        () => this.heroService.getHeroes().pipe(map((h) => h.splice(0, 4))),
        {
          refetchOnWindowFocus: true,
        },
      )
    
      constructor(private heroService: HeroService) {}
    }
    

    조회는 변이할 수 있다


    데이터 획득 외에도 mutate 방법으로 데이터를 저장할 수 있는 API가 제공됩니다.
    여기에서 rx-query는 낙관적인 업데이트를 사용하기 때문에 응용 프로그램을 더욱 빨리 느끼게 하는 데 도움이 된다.
    이것은 캐시의 상태가 서버에 요청하기 전에 업데이트된다는 것을 의미합니다.
    요청이 실패하면 캐시가 자동으로 이전 상태로 롤백됩니다.
    상태를 변경하려면 구성해야 합니다mutator:
    export class HeroDetailComponent {
      hero$ = query(
        'hero',
        +this.route.snapshot.paramMap.get('id'),
        (id) => this.heroService.getHero(id),
        {
          mutator: (hero) => {
            return this.heroService.updateHero(hero).pipe(tap(() => this.goBack()))
          },
        },
      )
    
      constructor(
        private route: ActivatedRoute,
        private heroService: HeroService,
        private location: Location,
      ) {}
    
      goBack(): void {
        this.location.back()
      }
    }
    
    변이를 호출하려면 mutateQueryOutput 방법을 사용하고 업데이트된 실체를 매개 변수로 합니다.
    <ng-container *ngIf="hero$ | async as hero">
      <ng-container [ngSwitch]="hero.status">
        <div class="heroes" *ngSwitchDefault>
          <h2>{{ hero.data.name | uppercase }} Details</h2>
          <div><span>id: </span>{{ hero.data.id }}</div>
          <div>
            <label
              >name:
              <input #name [value]="hero.data.name" placeholder="name" />
            </label>
          </div>
          <button (click)="goBack()">go back</button>
          <button (click)="hero.mutate({ id: hero.data.id, name: name.value })">
            save
          </button>
        </div>
    
        <div *ngSwitchCase="'loading'">Loading ...</div>
        <div *ngSwitchCase="'error'">Error ... ({{ hero.error }})</div>
      </ng-container>
    </ng-container>
    

    업데이트 방법


    위의 GIF에 문제가 표시됩니다.영웅의 상세한 정보가 업데이트되었지만, 계기판은 여전히 업데이트 전의 영웅의 상세한 정보를 표시합니다.
    업데이트는 영웅 목록 조회를 새로 고친 후에만 계기판에 표시됩니다.
    이에 따라 rx-query 수동으로 상태를 업데이트하는 조수 방법을 공개했다.
    export class HeroDetailComponent {
      hero$ = query(
        'hero',
        +this.route.snapshot.paramMap.get('id'),
        (id) => this.heroService.getHero(id),
        {
          mutator: (hero) => {
            const updater = (heroes: Hero[]) => {
              return heroes.map((h) => (h.id === hero.id ? hero : h))
            }
            mutateOptimistic('heroes-dashboard', updater)
    
            return this.heroService.updateHero(hero).pipe(
              tap(() => mutateSuccess('heroes-dashboard')),
              tap(() => this.goBack()),
              catchError((err) => {
                mutateError('heroes-dashboard', err)
                return throwError(err)
              }),
            )
          },
        },
      )
    
      constructor(
        private route: ActivatedRoute,
        private heroService: HeroService,
        private location: Location,
      ) {}
    
      goBack(): void {
        this.location.back()
      }
    }
    

    끝내다

    rx-query 여러 가지 장점이 있는데 모두 사용자 체험을 개선하고 좋은 개발자인 인간공학을 명심하기 위해서이다.
    기능적으로 볼 때 캐시와 리셋 설정 옵션은 응용 프로그램을 더욱 빨리 느끼게 하고, 자동 재시도는 응용 프로그램을 더욱 튼튼하게 하는 데 도움이 된다.
    개발자는 모든 상황이 서로 다른 처리를 받을 수 있기 때문에 설정 가능한 옵션을 받아 조회를 조정할 수 있다.rx-query 개발자로 하여금 어떻게 상태를 사용하는지 생각하게 한다.사용rx-query 시 다음과 같은 문제가 발생합니다.그것은 캐시되어야 합니까? 만약 그렇다면, 캐시는 얼마나 걸립니까?상태를 언제 갱신하고 싶습니까?요청이 실패했을 때, 우리는 무엇을 해야 합니까?
    《영웅의 여행》의 장점은 명백히 알 수 있다.
  • 응용 프로그램의 속도가 더 빠르다.
  • 부품 코드가 재단된다.
  • 요청 처리 단계별 템플릿 코드(HTML)가 증가합니다.
  • 캐시 상태를 사용할 때 상태를 신선하게 유지하기 쉽다.
  • 참고로 rx-query 구성 요소 수준의 HTTP 요청뿐만 아니라 간단한 패키지도 가능합니다.
    다음을 수행할 수 있습니다.
  • 또한 다른 패키지와 결합하여 사용할 수 있다. 예를 들어 @ngrx/component-store와 결합하여 사용할 수 있다. 그 중에서 조회 출력
  • 을 바탕으로 구성 요소 상태를 업데이트할 수 있다.
  • 는 쿼리 출력
  • 을 기반으로 스케줄링 작업을 수행하는 등 글로벌 수준에서 사용할 수도 있습니다.
    사용rx-query을 시작하려면 다음 명령을 사용하여 패키지를 설치합니다.
    npm install rx-query
    
    그것의 실제 상황을 이해하려면 @ngrx/effects 를 보십시오.
    이 예시된 소스 코드는 live examples 에서 찾을 수 있습니다.
    트위터에서 저를 팔로우해 구독repository이 최초로 Newsletter에 발표되었습니다.

    좋은 웹페이지 즐겨찾기