Angular 2+ - 数据加载:最佳实践

Posted

技术标签:

【中文标题】Angular 2+ - 数据加载:最佳实践【英文标题】:Angular 2+ - data loading: best practices 【发布时间】:2018-12-06 16:08:28 【问题描述】:

我想知道在 Angular 5 中加载数据的最佳方法是什么。当然,我们希望让组件保持不变;)现在,我正在使用解析器,Angular 在特定路由中调用解析器。基本上,数据是在组件初始化之前加载的,因此我可以向用户显示加载器动画。

例如。

AResolver.resolver.ts

@Injectable()
export class AResolver implements Resolve<any> 
    constructor(private readonly aService: AService) 

    resolve(route: ActivatedRouteSnapshot) 
        return this.aService.find(route.paramMap.get('id'));
    

M.module.ts

export const MRoutes: Routes = [
    
        path: 'route-x',
        component: AComponent,
        resolve: AResolver
    
];

AComponent.component.ts

@Component(
    selector: 'a-super-fancy-name',
)
export class AComponent 

    constructor(private readonly route: ActivatedRoute) 


嗯,到目前为止还不错,但是:

    如果我有具有依赖关系的解析器怎么办?那么,要解析 B,我们需要来自 A 的 id 吗?我应该使用解析器包装器吗?因此我会这样做:

示例

@Injectable()
export class ABResolver implements Resolve<any> 

    constructor(private readonly aService: AService, private readonly bService: BService) 

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) 
        return this.aService.resolve(route, state).map((result) => this.bService.resolveWithId(result.Id));
    

我想这是最好的方法,因为我们仍然有 2 个可以重复使用的独立解析器。一般来说,为所有需要数据的组件或仅为依赖于其他解析器的解析器制作解析器包装器是一种好习惯,还是根本不应该使用解析器包装器?

    解析器中的其他方法(如resolveWithId)呢?

    AResolver route.paramMap 中用于获取id。传递 id 不是更好吗,因为这看起来是紧密耦合的?

    有哪些替代方案?

    我使用的方法有哪些缺点?

【问题讨论】:

解析器可能会有所改进。但是由于(IMO)目前的巨大限制,我现在几乎从不使用它们。真的感觉它不能很好地与流和反应式应用程序一起玩。在此处查看更多信息***.com/questions/49054232/… 我通常使用 ngrx,因此,我确实管理所有 HTTP 调用以使其生效。 【参考方案1】:

解析器在纸面上很好,但只有当您可以轻松地从当前路由中获取所有参数时,不幸的是这种情况很少发生,有时您必须访问父路由,有时您不想将所有信息存储在路由中参数,有时您不想阻止导航,而且路由器参数不是很灵活的功能,它可能很容易使重构复杂化(您不能简单地更改父路由参数而不影响子解析器,并且编译器无法检查错误)。

角度加载数据没有任何预定义/推荐的方式。根据应用程序规模,您可以根据需要简单地在每个组件中加载数据,或者强加某些规则或通用接口/抽象类,或者使用 rxjs 等缓存共享服务。 基本上你只能在自定义架构和类似 Redux 的 ngrx 方法之间进行选择。 ngrx 显然设置了一些限制和限制,并提供了结构和总体方向。

【讨论】:

感谢您的回答,但我正在寻找最佳实践和权衡。我想听听常见的陷阱以及为什么我应该 x 而不是 y 的原因:)【参考方案2】:

您可以改为使用InjectionTokens。

export const DI_ROUTE_ID = new InjectionToken<Observable<string>>('Route id$');

export function factoryRouteId(routeService: WhateverRouteParamObserver): Observable<string> 
  return routeService.params$.pipe(map((params) => params?.id ?? null));


export const DI_ROUTE_ID_PROVIDER: Provider = 
  provide: DI_ROUTE_ID,
  deps: [WhateverRouteParamObserver],
  useFactory: factoryRouteId,
;

在某个模块中提供它(可能直接在 app.module 中),即:

@NgModule(
  providers: [
    ...
    DI_ROUTE_ID_PROVIDER,
  ],
)

并在需要的地方注入。

export class DetailComponent 
  constructor(
    @Inject(DI_ROUTE_ID) private readonly id$: Observable<string>,
  ) 
...

您还可以创建另一个 DI 令牌,该令牌依赖于此 ID 令牌和 http 服务,并直接提供 Observable&lt;WhateverDetailData&gt;(您可以直接在令牌中 shareReplay)。

export const DI_ROUTE_DETAILS = new InjectionToken<Observable<DetailsData>>('Route details$');

export function factoryRouteDetails(id$: Observable<string>, httpDataService: HttpDataService): Observable<DetailsData> 
  return id$.pipe(
    switchMap((id) => httpDataService.getData$(id).pipe(
      catchError(() => of(null))
    ),
    shareReplay(refCount: true, bufferSize: 1),
  );


export const DI_ROUTE_DETAILS_PROVIDER: Provider = 
  provide: DI_ROUTE_DETAILS,
  deps: [DI_ROUTE_ID, HttpDataService],
  useFactory: factoryRouteDetails,
;

【讨论】:

以上是关于Angular 2+ - 数据加载:最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

Angularjs 数据存储最佳实践

Atitit.angular.js 使用最佳实践 原理与常见问题解决与列表显示案例 attilax总结

Angular4最佳实践之unsubscribe

雪花数据加载最佳实践规范化还是非规范化?

在数据块中加载增量表特定分区的最佳实践是啥?

考虑 application.properties 的预加载数据的 Spring 数据最佳实践