perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting in not needed Reflect polyfil and smaller bundles. Code savings for HelloWorld using Closure: Reflective: bundle.js: 105,864(34,190 gzip) Static: bundle.js: 154,889(33,555 gzip) 645( 2%) BREAKING CHANGE: `platformXXXX()` no longer accepts providers which depend on reflection. Specifically the method signature when from `Provider[]` to `StaticProvider[]`. Example: Before: ``` [ MyClass, {provide: ClassA, useClass: SubClassA} ] ``` After: ``` [ {provide: MyClass, deps: [Dep1,...]}, {provide: ClassA, useClass: SubClassA, deps: [Dep1,...]} ] ``` NOTE: This only applies to platform creation and providers for the JIT compiler. It does not apply to `@Compotent` or `@NgModule` provides declarations. Benchpress note: Previously Benchpress also supported reflective provides, which now require static providers. DEPRECATION: - `ReflectiveInjector` is now deprecated as it will be remove. Use `Injector.create` as a replacement. closes #18496
This commit is contained in:

committed by
Victor Berchet

parent
d9d00bd9b5
commit
fcadbf4bf6
@ -19,7 +19,7 @@ import {isPromise} from '../src/util/lang';
|
||||
import {ApplicationInitStatus} from './application_init';
|
||||
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
|
||||
import {Console} from './console';
|
||||
import {Injectable, InjectionToken, Injector, Provider, ReflectiveInjector} from './di';
|
||||
import {Injectable, InjectionToken, Injector, StaticProvider} from './di';
|
||||
import {CompilerFactory, CompilerOptions} from './linker/compiler';
|
||||
import {ComponentFactory, ComponentRef} from './linker/component_factory';
|
||||
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||
@ -99,17 +99,18 @@ export function createPlatform(injector: Injector): PlatformRef {
|
||||
* @experimental APIs related to application bootstrap are currently under review.
|
||||
*/
|
||||
export function createPlatformFactory(
|
||||
parentPlatformFactory: ((extraProviders?: Provider[]) => PlatformRef) | null, name: string,
|
||||
providers: Provider[] = []): (extraProviders?: Provider[]) => PlatformRef {
|
||||
parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef) | null,
|
||||
name: string, providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) =>
|
||||
PlatformRef {
|
||||
const marker = new InjectionToken(`Platform: ${name}`);
|
||||
return (extraProviders: Provider[] = []) => {
|
||||
return (extraProviders: StaticProvider[] = []) => {
|
||||
let platform = getPlatform();
|
||||
if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
|
||||
if (parentPlatformFactory) {
|
||||
parentPlatformFactory(
|
||||
providers.concat(extraProviders).concat({provide: marker, useValue: true}));
|
||||
} else {
|
||||
createPlatform(ReflectiveInjector.resolveAndCreate(
|
||||
createPlatform(Injector.create(
|
||||
providers.concat(extraProviders).concat({provide: marker, useValue: true})));
|
||||
}
|
||||
}
|
||||
@ -292,8 +293,7 @@ export class PlatformRef_ extends PlatformRef {
|
||||
// Attention: Don't use ApplicationRef.run here,
|
||||
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
|
||||
return ngZone.run(() => {
|
||||
const ngZoneInjector =
|
||||
ReflectiveInjector.resolveAndCreate([{provide: NgZone, useValue: ngZone}], this.injector);
|
||||
const ngZoneInjector = Injector.create([{provide: NgZone, useValue: ngZone}], this.injector);
|
||||
const moduleRef = <InternalNgModuleRef<M>>moduleFactory.create(ngZoneInjector);
|
||||
const exceptionHandler: ErrorHandler = moduleRef.injector.get(ErrorHandler, null);
|
||||
if (!exceptionHandler) {
|
||||
|
@ -6,9 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Optional, Provider, SkipSelf} from '../../di';
|
||||
import {Optional, SkipSelf, StaticProvider} from '../../di';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
|
||||
|
||||
/**
|
||||
* A type describing supported iterable types.
|
||||
*
|
||||
@ -181,7 +182,7 @@ export class IterableDiffers {
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
static extend(factories: IterableDifferFactory[]): Provider {
|
||||
static extend(factories: IterableDifferFactory[]): StaticProvider {
|
||||
return {
|
||||
provide: IterableDiffers,
|
||||
useFactory: (parent: IterableDiffers) => {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Optional, Provider, SkipSelf} from '../../di';
|
||||
import {Optional, SkipSelf, StaticProvider} from '../../di';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
|
||||
|
||||
@ -156,7 +156,7 @@ export class KeyValueDiffers {
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
static extend<S>(factories: KeyValueDifferFactory[]): Provider {
|
||||
static extend<S>(factories: KeyValueDifferFactory[]): StaticProvider {
|
||||
return {
|
||||
provide: KeyValueDiffers,
|
||||
useFactory: (parent: KeyValueDiffers) => {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable, InjectionToken} from '../di';
|
||||
import {Injectable, InjectionToken, StaticProvider} from '../di';
|
||||
import {MissingTranslationStrategy} from '../i18n/tokens';
|
||||
import {ViewEncapsulation} from '../metadata';
|
||||
import {Type} from '../type';
|
||||
@ -14,6 +14,7 @@ import {Type} from '../type';
|
||||
import {ComponentFactory} from './component_factory';
|
||||
import {NgModuleFactory} from './ng_module_factory';
|
||||
|
||||
|
||||
/**
|
||||
* Combination of NgModuleFactory and ComponentFactorys.
|
||||
*
|
||||
@ -101,7 +102,7 @@ export type CompilerOptions = {
|
||||
useDebug?: boolean,
|
||||
useJit?: boolean,
|
||||
defaultEncapsulation?: ViewEncapsulation,
|
||||
providers?: any[],
|
||||
providers?: StaticProvider[],
|
||||
missingTranslation?: MissingTranslationStrategy,
|
||||
// Whether to support the `<template>` tag and the `template` attribute to define angular
|
||||
// templates. They have been deprecated in 4.x, `<ng-template>` should be used instead.
|
||||
|
@ -9,22 +9,16 @@
|
||||
import {PlatformRef, PlatformRef_, createPlatformFactory} from './application_ref';
|
||||
import {PLATFORM_ID} from './application_tokens';
|
||||
import {Console} from './console';
|
||||
import {Provider} from './di';
|
||||
import {Reflector, reflector} from './reflection/reflection';
|
||||
import {Injector, StaticProvider} from './di';
|
||||
import {TestabilityRegistry} from './testability/testability';
|
||||
|
||||
function _reflector(): Reflector {
|
||||
return reflector;
|
||||
}
|
||||
|
||||
const _CORE_PLATFORM_PROVIDERS: Provider[] = [
|
||||
const _CORE_PLATFORM_PROVIDERS: StaticProvider[] = [
|
||||
// Set a default platform name for platforms that don't set it explicitly.
|
||||
{provide: PLATFORM_ID, useValue: 'unknown'},
|
||||
PlatformRef_,
|
||||
{provide: PlatformRef_, deps: [Injector]},
|
||||
{provide: PlatformRef, useExisting: PlatformRef_},
|
||||
{provide: Reflector, useFactory: _reflector, deps: []},
|
||||
TestabilityRegistry,
|
||||
Console,
|
||||
{provide: TestabilityRegistry, deps: []},
|
||||
{provide: Console, deps: []},
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -190,4 +190,4 @@ export function getQueryValue(
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ function validateNode(parent: NodeDef | null, node: NodeDef, nodeCount: number)
|
||||
const parentFlags = parent ? parent.flags : 0;
|
||||
if ((parentFlags & NodeFlags.TypeElement) === 0) {
|
||||
throw new Error(
|
||||
`Illegal State: Provider/Directive nodes need to be children of elements or anchors, at index ${node.index}!`);
|
||||
`Illegal State: StaticProvider/Directive nodes need to be children of elements or anchors, at index ${node.index}!`);
|
||||
}
|
||||
}
|
||||
if (node.query) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ReflectiveInjector} from '@angular/core';
|
||||
import {Injector} from '@angular/core';
|
||||
import {IterableDiffers} from '@angular/core/src/change_detection/differs/iterable_differs';
|
||||
|
||||
import {SpyIterableDifferFactory} from '../../spies';
|
||||
@ -50,7 +50,7 @@ export function main() {
|
||||
|
||||
describe('.extend()', () => {
|
||||
it('should throw if calling extend when creating root injector', () => {
|
||||
const injector = ReflectiveInjector.resolveAndCreate([IterableDiffers.extend([])]);
|
||||
const injector = Injector.create([IterableDiffers.extend([])]);
|
||||
|
||||
expect(() => injector.get(IterableDiffers))
|
||||
.toThrowError(/Cannot extend IterableDiffers without a parent injector/);
|
||||
@ -58,9 +58,8 @@ export function main() {
|
||||
|
||||
it('should extend di-inherited differs', () => {
|
||||
const parent = new IterableDiffers([factory1]);
|
||||
const injector =
|
||||
ReflectiveInjector.resolveAndCreate([{provide: IterableDiffers, useValue: parent}]);
|
||||
const childInjector = injector.resolveAndCreateChild([IterableDiffers.extend([factory2])]);
|
||||
const injector = Injector.create([{provide: IterableDiffers, useValue: parent}]);
|
||||
const childInjector = Injector.create([IterableDiffers.extend([factory2])], injector);
|
||||
|
||||
expect(injector.get(IterableDiffers).factories).toEqual([factory1]);
|
||||
expect(childInjector.get(IterableDiffers).factories).toEqual([factory2, factory1]);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Compiler, ComponentFactory, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core';
|
||||
import {Compiler, ComponentFactory, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, SkipSelf} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver';
|
||||
@ -1850,8 +1850,7 @@ class DynamicViewport {
|
||||
const myService = new MyService();
|
||||
myService.greeting = 'dynamic greet';
|
||||
|
||||
this.injector = ReflectiveInjector.resolveAndCreate(
|
||||
[{provide: MyService, useValue: myService}], vc.injector);
|
||||
this.injector = Injector.create([{provide: MyService, useValue: myService}], vc.injector);
|
||||
this.componentFactory =
|
||||
componentFactoryResolver.resolveComponentFactory(ChildCompUsingService) !;
|
||||
}
|
||||
|
@ -163,13 +163,19 @@ export function main() {
|
||||
|
||||
// root elements
|
||||
expect(() => createAndGetRootNodes(compViewDef(nodes)))
|
||||
.toThrowError('No provider for Dep!');
|
||||
.toThrowError(
|
||||
'StaticInjectorError[Dep]: \n' +
|
||||
' StaticInjectorError[Dep]: \n' +
|
||||
' NullInjectorError: No provider for Dep!');
|
||||
|
||||
// non root elements
|
||||
expect(
|
||||
() => createAndGetRootNodes(compViewDef(
|
||||
[elementDef(NodeFlags.None, null !, null !, 4, 'span')].concat(nodes))))
|
||||
.toThrowError('No provider for Dep!');
|
||||
.toThrowError(
|
||||
'StaticInjectorError[Dep]: \n' +
|
||||
' StaticInjectorError[Dep]: \n' +
|
||||
' NullInjectorError: No provider for Dep!');
|
||||
});
|
||||
|
||||
it('should inject from a parent element in a parent view', () => {
|
||||
@ -191,7 +197,10 @@ export function main() {
|
||||
elementDef(NodeFlags.None, null !, null !, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null !, 0, SomeService, ['nonExistingDep'])
|
||||
])))
|
||||
.toThrowError('No provider for nonExistingDep!');
|
||||
.toThrowError(
|
||||
'StaticInjectorError[nonExistingDep]: \n' +
|
||||
' StaticInjectorError[nonExistingDep]: \n' +
|
||||
' NullInjectorError: No provider for nonExistingDep!');
|
||||
});
|
||||
|
||||
it('should use null for optional missing dependencies', () => {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, SkipSelf, Type, ɵDepFlags as DepFlags, ɵERROR_COMPONENT_TYPE, ɵNodeFlags as NodeFlags, ɵclearProviderOverrides as clearProviderOverrides, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
|
||||
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, Type, ɵDepFlags as DepFlags, ɵERROR_COMPONENT_TYPE, ɵNodeFlags as NodeFlags, ɵclearProviderOverrides as clearProviderOverrides, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
|
||||
|
||||
import {AsyncTestCompleter} from './async_test_completer';
|
||||
import {ComponentFixture} from './component_fixture';
|
||||
@ -308,8 +308,8 @@ export class TestBed implements Injector {
|
||||
}
|
||||
}
|
||||
const ngZone = new NgZone({enableLongStackTrace: true});
|
||||
const ngZoneInjector = ReflectiveInjector.resolveAndCreate(
|
||||
[{provide: NgZone, useValue: ngZone}], this.platform.injector);
|
||||
const ngZoneInjector =
|
||||
Injector.create([{provide: NgZone, useValue: ngZone}], this.platform.injector);
|
||||
this._moduleRef = this._moduleFactory.create(ngZoneInjector);
|
||||
// ApplicationInitStatus.runInitializers() is marked @internal to core. So casting to any
|
||||
// before accessing it.
|
||||
|
Reference in New Issue
Block a user