由路由守卫引发的Angular DI inject思考
在我修改以前模块的时候,发现路由守卫中的CanActivate已经标记废弃了,现在官网中推荐的是一种模版代码更少的写法:
export const yourGuard: CanActivateFn = (
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) => {
// your logic goes here
}
秉承及时更新代码的原则,我立即对该模块中的路由守卫代码进行了更改,但在修改时遇到了一个问题,即判断逻辑依赖于其他注入,这没有constructor你让我很难办啊 。于是我只好再探官网,发现可以使用 inject 来引用其他依赖项(例: const router = inject(Router)
)。
那么这个inject是什么东东呢?
众所周知,在 Angular 中的组件/指令/管道中如果需要使用某个服务,必须通过构造函数参数注入才可使用。
import { Component } from '@angular/core';
import { Injectable } from '@angular/core';
@Injectable()
export class HeroService {
constructor() {}
}
@Component({
selector: 'app-inject',
template: ``,
standalone: true,
providers: [HeroService],
})
export class InjectComponent {
constructor(private heroService: HeroService) {}
}
如果创建的服务没有通过@Injectable注解进行依赖注入,那么就需要创建一个InjectionToken,此外还需要使用@Inject()注解来在构造函数中引用该依赖项
import { Component, Inject } from '@angular/core';
import { InjectionToken } from '@angular/core';
export const InjectToken = new InjectionToken<InjectToken>(
'Hero_Inject_Token'
);
export class HeroService {
constructor() {}
}
@Component({
selector: 'app-inject',
template: ``,
standalone: true,
providers: [
{
provide: InjectToken,
useClass: HeroService,
},
],
})
export class InjectComponent {
constructor(
@Inject(InjectToken) private heroService: HeroService
) {}
}
回顾完DI的基础知识,再来看一下官方是怎样解释的
inject可以在以下四种情况使用
在为 Provider 的 @ Injectable useFactory 的工厂函数中
在为 InjectionToken 指定的 factory 函数中。
export const NameToken = new InjectionToken<string>('NAME_TOKEN');
@Injectable()
export class HeroService {
name = 'hero';
constructor() {}
}
@Injectable()
export class SecretHeroService {
name = 'secretHero';
constructor() {}
}
providers: [
SecretHeroService,
//在为 InjectionToken 指定的 factory 函数中
{
provide: NameToken,
useFactory: () => {
return inject(HeroService).name;
},
},
//在为Provider的Injectable useFactory的工厂函数中
{
provide: HeroService,
useFactory: () => {
return inject(SecretHeroService);
},
},
],
由 DI 系统实例化的类的构造(通过 constructor ),例如 @ Injectable 或 @ Component 。
constructor() {
const service = inject(HeroService);
const heroName = service.name
}
在此类类的字段的初始化器中。
name = inject(HeroService).name;
我们可以拿它干啥?
可以更轻松地在组件之间共享依赖项,并减少对父组件的依赖性。例如,在父组件中使用多个服务时,子组件可以使用 angular inject
注入这些服务,就可以不必手动注入每个服务。
可以让更加方便地在子组件中注入父组件中使用的服务,而不必关心父组件到底使用了哪些注入项。这样的设计可以提高代码的可重用性,使得子组件的实现更加独立,不会被父组件的变化所影响
未使用inject
@Injectable({ providedIn: 'root'})
export class childService extends ParentService {
constructor(public http: HttpClient) {
super(http);
}
}
@Injectable({ providedIn: 'root'})
export class ParentService {
constructor(public http: HttpClient) {}
}
使用inject
@Injectable({ providedIn: 'root' })
export class childService extends ParentService {
constructor() {
super();
}
}
@Injectable({ providedIn: 'root' })
export class ParentService {
http = inject(HttpClient);
constructor() {}
}