本周遇到的一些问题汇总

问题一:如何引用私有库
起初认为既然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的机制
本周遇到的一些问题汇总
文章图片

返还了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给下一个拦截器。

    推荐阅读