Angular中的`forwardRef`是什么,为什么我们需要它

它是什么

让我们从forwardRef的官方Angular 文档开始 。 它说如下:

允许引用尚未定义的引用。
例如,当声明需要为DI引用的令牌但尚未定义该令牌时,将使用forwardRef。 当我们尚未定义创建查询时使用的令牌时,也会使用它。

该定义讨论了对类的引用,并以引用类的标记为例。 在Angular中,我们定义了这样的依赖关系:

const dependency = { provide: SomeTokenClass, useClass: SomeProviderClass };

在上面的示例中,为provide和token指定了一个令牌。 因此,根据定义,我们知道可以将forwardRef用作令牌,如下所示:

const dependency = { provide: forwardRef(()=>{ SomeTokenClass }), useClass: SomeProviderClass };

但是,我们在useClass配方中也引用了SomeProviderClass类。 我们也可以对提供商使用这种方法吗? 文档没有提及,但是我们知道useClass配方拥有对类的引用 ,并且我们了解到forwardRef可以应用于引用。 因此答案是肯定的,我们也可以将这种方法应用于提供者配方:

const dependency = { provide: forwardRef(()=>{ SomeTokenClass }), useClass: forwardRef(()=>{ SomeProviderClass }) };

但是,仅当配方隐含对类的引用时才可以应用它,对于useClass或useExisting就是这种情况,如以下示例所示:

const dependency = { provide: forwardRef(()=>{ SomeTokenClass }), useExisting: forwardRef(()=>{ SomeOtherClassToken }) };

另外,如果您使用Inject装饰器按类引用注入令牌,则也可以应用该函数:

export class ADirective { constructor(@Inject(forwardRef(() => Token)) service) {

使用范例

Angular文档显示以下示例:

class Door { lock: Lock; // Door attempts to inject Lock, // despite it not being defined yet. // forwardRef makes this possible. constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { this.lock = lock; } } // Only at this point Lock is defined. class Lock {}

但是对我来说这是一个不自然的例子。 尽管可以理解要点,但是当我需要在实际应用程序中使用forwardRef时,很难通过查看来理解它。 我可以简单地将Lock类放在Door之上,这样就可以解决问题。 碰巧的是,Angular源提供了一个更好的真实单词用法示例。

您可能知道Angular表单具有ngModel和formControl指令,您可以在表单输入上使用它们。 这些控件中的每个控件都定义了一个提供程序,该提供程序允许通过公共令牌NgModel访问指令实例。 因此,例如,如果您要访问与自定义指令中的输入关联的form指令,则可以执行以下操作:

@Directive({ selector: '[mycustom]' }) export class MyCustom { constructor(@Inject(NgControl) directive) { ... <input type="text" ngModel mycustom>

为了使NgModel和formControl指令都定义一个formControlBinding提供程序并将其注册在指令装饰器描述符中。 这是formControl指令执行此操作的方式:

export const formControlBinding : any = { provide: NgControl, useExisting: FormControlDirective }; @Directive({ selector: '[formControl]', providers: [ formControlBinding ], ... }) export class FormControlDirective { ... }

和NgModel指令:

export const formControlBinding : any = { provide: NgControl, useExisting: NgModel }; @Directive({ selector: '[ngModel]', providers: [ formControlBinding ], ... }) export class NgModel { ... }

这里有趣的实现是formControlBinding是在指令类装饰器之外定义的。 因此,当JS运行时评估定义formControlBinding对象的代码时,尚未评估NgModel类定义,并且如果我们将提供者对象登录到控制台,我们将看到以下内容:

Object {useExisting: undefined, token: function}

嗯,useExisting指向undefined,因此Angular将无法解析其他标记。 这就是为什么Angular在这里使用forwardRef的原因:

export const formControlBinding: any = { provide: NgControl, useExisting: forwardRef (() => FormControlDirective ) }; export class FormControlDirective { ... } export const formControlBinding: any = { provide: NgControl, useExisting: forwardRef (() => FormControlDirective ) }; export class FormControlDirective { ... } ... export const formControlBinding: any = { provide: NgControl, useExisting: forwardRef (() => NgModel ) }; export class NgModel { ... } export const formControlBinding: any = { provide: NgControl, useExisting: forwardRef (() => NgModel ) }; export class NgModel { ... }

但是,如果我们在Angular的类装饰器中定义formControlBinding而不使用forwardRef的话,它会起作用吗:

@Directive({ selector: '[ngModel]', providers: [ { provide: NgControl, useExisting: NgModel } ], ... }) export class NgModel { ... }

好吧,如果您看一下代码,则似乎在装饰器中在类定义之前引用了NgModel。 但是您应该记住, 定义了类 ,所有类装饰器都将应用于该类。 因此,即使没有forwardRef,上述实现也可以工作。 但是,通过在装饰器中内联提供程序,我们将不再将其导出,因此无法在应用程序中重用。

为什么forwardRef起作用?

现在,您的脑海中可能浮现出一个问题,forwardRef是如何工作的。 实际上,这与JavaScript中的闭包如何工作有关。 当您在闭包函数中捕获变量时 ,它将捕获变量引用 ,而不是变量值 。 这是一个小例子,以证明这一点:

let a; function enclose() { console.log(a); } enclose(); // undefined a = 5; enclose(); // 5

您可以看到,尽管在创建封闭函数时未定义变量a,但它捕获了变量引用。 因此,稍后将变量更新为5时,它将记录正确的值。

而forwardRef只是一个将类引用捕获到闭包中的函数,并且在执行该函数之前定义了类。 Angular编译器在运行时使用函数resolveForwardRef解开令牌或提供程序类型。

您发现文章中的信息有帮助吗?

From: https://hackernoon.com/what-is-forwardref-in-angular-and-why-we-need-it-6ecefb417d48

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值