byg*_*ace 7 angular-cli angular2-aot angular
我看到奇怪的行为,因为通过解构赋值(或Object.assign)添加到对象的属性在传递时会出现,forRoot但在注入服务时不存在.此外,初始化后进行的更新在传递到forRoot服务时存在但在注入服务时不存在.这仅在使用AOT构建时发生.
我创建了一个重现问题的最小项目:https: //github.com/bygrace1986/wat
创建一个具有静态forRoot方法的模块,该方法将接受一个对象,然后通过它提供InjectionToken并注入到服务中.
TestModule
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TestDependency, TEST_DEPENDENCY, TestService } from './test.service';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class TestModule {
static forRoot(dependency?: TestDependency): ModuleWithProviders {
return {
ngModule: TestModule,
providers: [
TestService,
{ provide: TEST_DEPENDENCY, useValue: dependency }
]
}
}
}
Run Code Online (Sandbox Code Playgroud)
TestService的
import { Injectable, InjectionToken, Inject } from '@angular/core';
export interface TestDependency {
property: string;
}
export const TEST_DEPENDENCY = new InjectionToken<TestDependency>('TEST_DEPENDENCY');
@Injectable()
export class TestService {
constructor(@Inject(TEST_DEPENDENCY) dependency: TestDependency) {
console.log('[TestService]', dependency);
}
}
Run Code Online (Sandbox Code Playgroud)
此方案说明将非突变对象传递到forRoot正确注入依赖于它的服务.
要TestService在app.component.html(或将注入它的某个地方)设置引用.将依赖项传递给上的forRoot方法TestModule.
的AppModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { TestModule } from './test/test.module';
import { TestDependency } from './test/test.service';
const dependency = {
property: 'value'
} as TestDependency;
console.log('[AppModule]', dependency)
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
TestModule.forRoot(dependency)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)
跑ng serve --aot.
产量
[AppModule] {property: "value"}
[TestService] {property: "value"}
Run Code Online (Sandbox Code Playgroud)
此方案说明,在将提供的对象注入依赖于它的服务时,将忽略在初始化期间通过对象解构分配或在初始化之后进行的更改对对象所做的更改.
要设置创建具有其他属性的新对象,并使用对象解构将旧对象的属性分配给新对象.然后更新一个属性dependency并将其传递给该forRoot方法TestModule.
的AppModule
const dependency = {
property: 'value'
} as TestDependency;
const dependencyCopy = { id: 1, name: 'first', ...dependency };
dependencyCopy.name = 'last';
console.log('[AppModule]', dependencyCopy);
...
TestModule.forRoot(dependencyCopy)
Run Code Online (Sandbox Code Playgroud)
跑ng serve --aot.
产量
[AppModule] {id: 1, name: "last", property: "value"}
[TestService] {id: 1, name: "first"}
Run Code Online (Sandbox Code Playgroud)
意外结果
通过对象解构分配添加的任何属性都被删除,并且在对象传递到forRoot并注入之间之间还原了初始化之后所做的任何更新TestService.实际上,它不是事件相同的对象(我调试和检查===).就好像在使用分配或变异之前创建的原始对象......不知何故.
这种情况说明,当在AppModule级别而不是通过级别提供时,对象的突变不会被还原forRoot.
设置不要传递任何东西forRoot.而是使用注入令牌在提供AppModule者列表中提供对象.
的AppModule
imports: [
BrowserModule,
TestModule.forRoot()
],
providers: [
{ provide: TEST_DEPENDENCY, useValue: dependencyCopy }
],
Run Code Online (Sandbox Code Playgroud)
跑ng serve --aot.
产量
[AppModule] {id: 1, name: "last", property: "value"}
[TestService] {id: 1, name: "last", property: "value"}
Run Code Online (Sandbox Code Playgroud)
forRoot当对象注入依赖类时,为什么对通过get 提供的对象所做的更改会被还原?
--aot旗帜而不是更广泛的--prod旗帜再现.angular-cli项目的错误:https://github.com/angular/angular-cli/issues/10610forRoot我只引用变量时,得到这个:i0.?mpd(256, i6.TEST_DEPENDENCY, { id: 1, name: "first" }, [])]);.当它在AppModule提供者列表中被引用时,我得到了这个:i0.?mpd(256, i6.TEST_DEPENDENCY, i1.?0, [])]);然后在app模块中var ?0 = dependencyCopy; exports.?0 = ?0;.yur*_*zui 10
AOT的核心是元数据收集器.
它需要ts.SourceFile,然后递归遍历此文件的所有AST节点,并将所有节点转换为JSON表示.
收集器在文件的顶级检查以下类型的AST节点:
ts.SyntaxKind.ExportDeclaration
ts.SyntaxKind.ClassDeclaration
ts.SyntaxKind.TypeAliasDeclaration
ts.SyntaxKind.InterfaceDeclaration
ts.SyntaxKind.FunctionDeclaration
ts.SyntaxKind.EnumDeclaration
ts.SyntaxKind.VariableStatement
Run Code Online (Sandbox Code Playgroud)
Angular编译器还尝试使用所谓的Evaluator在运行时计算所有元数据,以便它可以理解在docs中监听的javascript表达式的子集
应该注意的是,编译器支持扩展运算符,但仅支持不在对象中的数组文字
const dependency = {
property: 'value' ===========> Non-exported VariableStatement
} as TestDependency; with value { property: 'value' }
imports: [
...,
TestModule.forRoot(dependency) ===========> Call expression with
ts.SyntaxKind.Identifier argument
which is calculated above
]
Run Code Online (Sandbox Code Playgroud)
请注意,参数只是静态对象值.
const dependency = {
property: 'value' ===========> Non-exported VariableStatement
} as TestDependency; with value { property: 'value' }
const dependencyCopy = {
id: 1, ============> Non-exported VariableStatement
name: 'first', with value { id: 1, name: 'first' }
...dependency (Spread operator is not supported here)
};
dependencyCopy.name = 'last'; ===========> ExpressionStatement is skipped
(see list of supported types above)
...
TestModule.forRoot(dependencyCopy) ===========> Call expression with
ts.SyntaxKind.Identifier argument
which is calculated above
Run Code Online (Sandbox Code Playgroud)
providers: [
{ provide: TEST_DEPENDENCY, useValue: dependencyCopy }
],
Run Code Online (Sandbox Code Playgroud)
在版本5中,使用变换器将角度移动到(几乎)本机TS编译过程并引入所谓的Lower Expressions变换器,这基本上意味着我们现在可以在装饰器元数据中使用箭头函数,例如:
providers: [{provide: SERVER, useFactory: () => TypicalServer}]
Run Code Online (Sandbox Code Playgroud)
将由角度编译器自动转换为以下内容:
export const ?0 = () => new TypicalServer();
...
providers: [{provide: SERVER, useFactory: ?0}]
Run Code Online (Sandbox Code Playgroud)
现在让我们阅读文档:
编译器特别处理包含字段useClass, useValue,useFactory和data的对象文字.编译器将 初始化其中一个字段的表达式转换为导出的变量,该变量将替换表达式.这个重写这些表达式的过程消除了对它们内容的所有限制,因为编译器不需要知道表达式的值 - 它只需要能够生成对值的引用.
我们可以看到angular现在使用dependencyCopy对象的引用.正如您已经注意到它将var ?0 = dependencyCopy;在生成的工厂中使用.
test.module.ts
export class TestModule {
static forRoot(dependency?: { data: TestDependency }): ModuleWithProviders {
return {
ngModule: TestModule,
providers: [
TestService,
{ provide: TEST_DEPENDENCY, useValue: dependency.data }
]
};
}
}
Run Code Online (Sandbox Code Playgroud)
app.module.ts
TestModule.forRoot({ data: dependencyCopy })
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
941 次 |
| 最近记录: |