本周遇到的一些问题汇总

问题一:如何引用私有库
起初认为既然node_modules是用来存放安装的包的,那么直接把 @yunzhi放进node_modules里就可以了。
尝试之后发现,虽然可以使用库中的函数,但是当直接引用函数时系统不自动引用@yunzhi,需要人工手动引入。
询问之后发现只需要根据引用的文件名在package.json中引入相应依赖,再重新执行npm install即可。
之后又发现package.json中主要包含以下几种参数

"scripts":{...}
"dependencies":{...}
"devDependencies":{...}

其中 scripts: 定义了一组可以运行的 node 脚本。

"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  }

也就是平常用到的ng s,ng t等。

dependencies 设置了作为依赖安装的 npm 软件包的列表。
比如:

"@angular/animations": "~12.1.2",
    "@angular/common": "~12.1.2",
    "@angular/compiler": "~12.1.2",
    "@angular/core": "~12.1.2",
    "@angular/forms": "~12.1.2",
"@yunzhi/ng-common": "0.0.6",
    "@yunzhi/ng-mock-api": "0.0.9",
    "@yunzhi/ng-theme": "0.0.8",
    "@yunzhi/utils": "0.0.3"

这些都是为我们的应用实际提供相应功能的依赖,也就是为MVC层提供服务的依赖。

devDependencies 设置了作为开发依赖安装的 npm 软件包的列表。
比如:

"karma": "~5.1.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.0.3",
    "karma-jasmine": "~4.0.0",
 "@angular/cli": "~12.1.2",
    "@angular/compiler-cli": "~12.1.2",
    "@types/jasmine": "~3.6.0",
    "@types/node": "^12.11.1",
    "@yunzhi/ng-router-testing": "0.0.2",

这些依赖都是在开发时用于测试时所需要的依赖,实际产品中用户并不会用到这些依赖。

知道了这些以后对于如何更改angular版本也就变得很简单,我们只需要改动带有@angular的依赖的版本即可。

问题二:在添加mockApi时遇到的问题
对于目前的使用来说只需要添加 qpis.ts, api.testing.modules, api.demo.modules
其中 apis.ts 也就是一个包含有我们所要用到的API的数组。

export const apis = [
  UserApi,
  LogApi,
  ClientApi
];

api.demo.module 用于ng s下脱离后台跑demo
api.testing.module用于进行单元测试。

providers: [
    {
      provide: HTTP_INTERCEPTORS, multi: true, useClass: MockApiTestingInterceptor.forRoot(apis)
    }]

这是api.testing中的主要内容,通过MockApiTestingInterceptorforRoot方法提供了HTTP_INTERCEPTORS拦截器服务。
Angular在发起Http请求时,会查询当前模块是否有HTTP_INTERCEPTORS的提供者, 如果有则会使用该提供者对请求进行拦截。
使用multi: true来表时当前提供者为多个具有这种能力的一个(使用该方案保证了可以定义多个拦截器)。

static forRoot(mockApis: Type[]): Type;
这是MockApiTestingInterceptor中的forRoot方法,接收一个API数组,返回一个HttpInterceptor

如图便是MockApi的机制

本周遇到的一些问题汇总_第1张图片

返还了HTTP_INTERCEPTORS拦截器服务,拦截到请求后就会根据forRoot方法中的api数组寻找相应的api.

Api大致格式

export class XxxApi implements MockApiInterface {
protected url = 'xxx'
getInjectors(): ApiInjector[] {
return [{
method: '',
url:  '',
result: (urlMatches: string[], options: { params: HttpParams; }){}
}]
}
}

其中urlMatches为请求URL的相关信息,options包括了接受的参数等信息。
至此,便可以实现拦截请求并返回模拟数据的功能。
这是就又出现了一个问题,为什么要在ng s中模拟返回数据不能像ng t中一样只添加一个MockApiTestingInterceptor提供的拦截器
于是我尝试着在app.modules中引用ApiTestingModule不引用ApiDemoModule,测试后发现出现了这样的报错 ——引用错误:beforeAll没有被定义。

main.js:1 Uncaught ReferenceError: beforeAll is not defined

ApiDemoModule:

providers: [
    {
      provide: HTTP_INTERCEPTORS, multi: true, useClass: LoadingInterceptor
    },
    {
      provide: HTTP_INTERCEPTORS, multi: true, useClass: MockApiInterceptor.forRoot(apis)
    }]

它比ApiTestingModule多了一个LoadingInterceptor拦截器:

export class LoadingInterceptor implements HttpInterceptor {
  public static loadingSubject = new Subject();
  public static loading$ = LoadingInterceptor.loadingSubject.asObservable();
  public static ignoreKey = 'loading-ignore';

  intercept(req: HttpRequest, next: HttpHandler): Observable> {
    if (req.params.has(LoadingInterceptor.ignoreKey)) {
      return next.handle(req);
    } else {
      LoadingInterceptor.loadingSubject.next(true);
      return next.handle(req).pipe(finalize(() => LoadingInterceptor.loadingSubject.next(false)));
    }
  }

}

其中 Subject 是一个特殊的Observable,它支持将值传给其他很多的Observable,
req.params.has(xxx)是用来检索req.params中是否含有xxx,如果没有则返回null,
handel()将HttpRequest转换为HttpEvents流,使其传向下一个拦截器
finalize返回一个Observable,该Observable镜像源Observable,但当源在完成或出错时终止时将调用指定函数。
之后经过测试在ng s中触发的是

return next.handle(req).pipe(finalize(() => LoadingInterceptor.loadingSubject.next(false)));

也就是说如果请求的参数含有loading-ignore说明没有错误,可以直接传向下一个拦截器,如果不含有则说明有错误,需要经过finalize管道的处理,返回一个执行时没有错误的Observable给下一个拦截器。

你可能感兴趣的