import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Routes, RouterModule } from '@angular/router';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
export const ROUTER_CONFIG: Routes = [
{ path: '', loadChildren: './home/home.module#HomeModule' },
{ path: 'about', loadChildren: './about/about.module#AboutModule' },
{ path: 'contact', loadChildren: './contact/contact.module#ContactModule' },
];
@NgModule({
imports: [BrowserModule, HttpModule, RouterModule.forRoot(ROUTER_CONFIG)],
bootstrap: [AppComponent],
declarations: [AppComponent],
})
export class AppModule {}
The above example code defines a root component and some routing rules, and through these rules, routes to different modules.
Angular will generate VM (virtual machine) friendly code to maximize its performance.
Angular will generate an injector for each of our modules ( module
), so in our case it will take AppModule (our decorator class) and create an injector called AppModuleInjector.
Here is the AppModule Injector code generated by the compiler:
import { NgModuleInjector } from '@angular/core/src/linker/ng_module_factory';
import { CommonModule } from '@angular/common/src/common_module';
import { ApplicationModule, _localeFactory } from '@angular/core/src/application_module';
import { BrowserModule, errorHandler } from '@angular/platform-browser/src/browser';
import { RouterModule, ROUTER_FORROOT_GUARD } from '@angular/router/src/router_module';
import { NgLocaleLocalization, NgLocalization } from '@angular/common/src/localization';
import { ApplicationInitStatus, APP_INITIALIZER } from '@angular/core/src/application_init';
import { Testability, TestabilityRegistry } from '@angular/core/src/testability/testability';
import { HttpModule } from '@angular/http/src/http_module';
import { ApplicationRef, ApplicationRef_ } from '@angular/core/src/application_ref';
import { BrowserModule } from '@angular/platform-browser/src/browser';
import { Injector } from '@angular/core/src/di/injector';
import { LOCALE_ID } from '@angular/core/src/i18n/tokens';
import { RouterModule, provideForRootGuard } from '@angular/router/src/router_module';
import { Router } from '@angular/router/src/router';
import { NgZone } from '@angular/core/src/zone/ng_zone';
import { Console } from '@angular/core/src/console';
import { ROUTES } from '@angular/router/src/router_config_loader';
import { ErrorHandler } from '@angular/core/src/error_handler';
import { AppModule } from './app.module';
import { AppComponentNgFactory } from './app.component.ngfactory';
class AppModuleInjector extends NgModuleInjector<AppModule> {
_CommonModule_0: CommonModule;
_ApplicationModule_1: ApplicationModule;
_BrowserModule_2: BrowserModule;
_ROUTER_FORROOT_GUARD_3: any;
_RouterModule_4: RouterModule;
_HttpModule_5: HttpModule;
_AppModule_6: AppModule;
_ErrorHandler_7: any;
_ApplicationInitStatus_8: ApplicationInitStatus;
_Testability_9: Testability;
_ApplicationRef__10: ApplicationRef_;
__ApplicationRef_11: any;
__ROUTES_12: any[];
constructor(parent: Injector) {
super(parent, [AppComponentNgFactory], [AppComponentNgFactory]);
}
get _ApplicationRef_11(): any {
if (this.__ApplicationRef_11 == null) {
this.__ApplicationRef_11 = this._ApplicationRef__10;
}
return this.__ApplicationRef_11;
}
get _ROUTES_12(): any[] {
if (this.__ROUTES_12 == null) {
this.__ROUTES_12 = [[
{
path: '', loadChildren: './home/home.module#HomeModule'
},
{
path: 'about', loadChildren: './about/about.module#AboutModule'
},
{
path: 'contact', loadChildren: './contact/contact.module#ContactModule'
}
]];
}
return this.__ROUTES_12;
}
createInternal(): AppModule {
this._CommonModule_0 = new CommonModule();
this._ApplicationModule_1 = new ApplicationModule();
this._BrowserModule_2 = new BrowserModule(this.parent.get(BrowserModule, (null as any)));
this._ROUTER_FORROOT_GUARD_3 = provideForRootGuard(this.parent.get(Router, (null as any)));
this._RouterModule_4 = new RouterModule(this._ROUTER_FORROOT_GUARD_3);
this._HttpModule_5 = new HttpModule();
this._AppModule_6 = new AppModule();
this._ErrorHandler_7 = errorHandler();
this._ApplicationInitStatus_8 = new ApplicationInitStatus(this.parent.get(APP_INITIALIZER, (null as any)));
this._Testability_9 = new Testability(this.parent.get(NgZone));
this._ApplicationRef__10 = new ApplicationRef_(
this.parent.get(NgZone),
this.parent.get(Console),
this,
this._ErrorHandler_7,
this,
this._ApplicationInitStatus_8,
this.parent.get(TestabilityRegistry, (null as any)),
this._Testability_9
);
return this._AppModule_6;
}
getInternal(token: any, notFoundResult: any): any {
if (token === CommonModule) { return this._CommonModule_0; }
if (token === ApplicationModule) { return this._ApplicationModule_1; }
if (token === BrowserModule) { return this._BrowserModule_2; }
if (token === ROUTER_FORROOT_GUARD) { return this._ROUTER_FORROOT_GUARD_3; }
if (token === RouterModule) { return this._RouterModule_4; }
if (token === HttpModule) { return this._HttpModule_5; }
if (token === AppModule) { return this._AppModule_6; }
if (token === ErrorHandler) { return this._ErrorHandler_7; }
if (token === ApplicationInitStatus) { return this._ApplicationInitStatus_8; }
if (token === Testability) { return this._Testability_9; }
if (token === ApplicationRef_) { return this._ApplicationRef__10; }
if (token === ApplicationRef) { return this._ApplicationRef_11; }
if (token === ROUTES) { return this._ROUTES_12; }
return notFoundResult;
}
destroyInternal(): void {
this._ApplicationRef__10.ngOnDestroy();
}
}
For readability, all the import code above has been changed from manual import to named import. In the actual generated code, each module is imported using wildcards to avoid naming conflicts.
For example, HttpModule would be imported like this:
import * as import6 from '@angular/http/src/http_module';
Then use import6.HttpModule instead of HttpModule to reference it.
We'll learn three things from the generated code above: class properties, module imports, and how the dependency injection mechanism works.
AppModuleInjector properties
Create properties on AppModuleInjector for each provider/dependency:
// ...
class AppModuleInjector extends NgModuleInjector<AppModule> {
_CommonModule_0: CommonModule;
_ApplicationModule_1: ApplicationModule;
_BrowserModule_2: BrowserModule;
// ...
}
Here's a snippet of the compilation output above - so we'll focus on the three properties defined on the class:
- CommonModule
- ApplicationModule
- BrowserModule
Our module only declares BrowserModule, so where do CommonModule and ApplicationModule come from? These are actually exported for us by the BrowserModule, so we don't need to import them ourselves.
A number is also appended to the end of each property in the module. As with wildcard imports, this is to avoid potential naming conflicts between providers.
We could import two modules that use a service with a shared name and without incrementing numbers, they will both be assigned to the same property, which could lead to further errors.
Module imports
When compiling, Angular uses the direct path to each provider it imports, for example, when we write the following code:
import { CommonModule } from '@angular/common';
The compiled code is actually:
import * as import5 from '@angular/common/src/common_module';
So when the code is compiled and bundled together, we can take advantage of tree-shaking and only include the parts of each module we actually use.
Dependency Injection
Each module handles its own dependency injection, if it has no dependencies, it goes to the parent module until it is found or not, in the latter case we get an error.
It's important to note that all dependencies use tokens to uniquely identify them, both at registration and lookup.
There are two different ways to start our dependencies, either in createInternal or as property getters.
For example, we use BrowserModule and HttpModule, which are created here:
class AppModuleInjector extends NgModuleInjector<AppModule> {
_CommonModule_0: CommonModule;
_ApplicationModule_1: ApplicationModule;
_BrowserModule_2: BrowserModule;
_HttpModule_5: HttpModule;
_AppModule_6: AppModule;
createInternal(): AppModule {
this._CommonModule_0 = new CommonModule();
this._ApplicationModule_1 = new ApplicationModule();
this._BrowserModule_2 = new BrowserModule(this.parent.get(BrowserModule, (null as any)));
this._HttpModule_5 = new HttpModule();
this._AppModule_6 = new AppModule();
// ...
return this._AppModule_6;
}
}
You can see that two exports of BrowserModule - CommonModule and ApplicationModule have been created, along with our other imported modules. Our actual module is also created (AppModule) so it can be used by other modules.
For all other providers, they are created when needed through getters in the class. This is to avoid creating instances of the provider when they are not needed, while improving initial rendering performance.
Whenever we talk about injectors in Angular, it refers to the code generated (compiled) from our modules.
When Angular looks for a dependency (such as the one we inject via the constructor), it looks in the module injector, and if it can't find it, it traverses up the parent module. If it doesn't exist, an error will be thrown.
When we use type definitions in constructors, Angular uses these types (i.e. classes) as markers for finding dependencies. This token is then passed to getInternal and returns an instance of the dependency (if it exists). Learn again through the source code:
class AppModuleInjector extends NgModuleInjector<AppModule> {
// new BrowserModule(this.parent.get(BrowserModule, (null as any)));
_BrowserModule_2: BrowserModule;
// new HttpModule()
_HttpModule_5: HttpModule;
// new AppModule()
_AppModule_6: AppModule;
getInternal(token: any, notFoundResult: any): any {
if (token === BrowserModule) {
return this._BrowserModule_2;
}
if (token === HttpModule) {
return this._HttpModule_5;
}
if (token === AppModule) {
return this._AppModule_6;
}
return notFoundResult;
}
}
So, in the getInternal method, you can see that Angular is checking our token with a simple if statement and will return the relevant properties of the provider - if found.
Otherwise, we'll use the returned notFoundResult to rescue the getInternal method. When Angular walks through our modules to find the required dependencies, this notFoundResult will be empty - until it finds the dependency, or it reaches the root module and still can't find it, at which point an error will be thrown.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。