perf(view): use pre-resolved bindings for child injector init
Creating a child injector from pre-resolved bindings (if any) is an order of magnitude faster.
This commit is contained in:
parent
c05bad381c
commit
308823b6ea
@ -1,5 +1,7 @@
|
|||||||
import {Type} from 'angular2/src/facade/lang';
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
|
import {List} from 'angular2/src/facade/collection';
|
||||||
import {Directive} from 'angular2/src/core/annotations/annotations'
|
import {Directive} from 'angular2/src/core/annotations/annotations'
|
||||||
|
import {ResolvedBinding} from 'angular2/di';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combination of a type with the Directive annotation
|
* Combination of a type with the Directive annotation
|
||||||
@ -7,9 +9,11 @@ import {Directive} from 'angular2/src/core/annotations/annotations'
|
|||||||
export class DirectiveMetadata {
|
export class DirectiveMetadata {
|
||||||
type:Type;
|
type:Type;
|
||||||
annotation:Directive;
|
annotation:Directive;
|
||||||
|
resolvedInjectables:List<ResolvedBinding>;
|
||||||
|
|
||||||
constructor(type:Type, annotation:Directive) {
|
constructor(type:Type, annotation:Directive, resolvedInjectables:List<ResolvedBinding>) {
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.resolvedInjectables = resolvedInjectables;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {Injectable} from 'angular2/di';
|
import {Injectable, Injector} from 'angular2/di';
|
||||||
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||||
import {Directive} from '../annotations/annotations';
|
import {Directive, Component} from '../annotations/annotations';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
import {DirectiveMetadata} from './directive_metadata';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
@ -13,7 +13,11 @@ export class DirectiveMetadataReader {
|
|||||||
var annotation = annotations[i];
|
var annotation = annotations[i];
|
||||||
|
|
||||||
if (annotation instanceof Directive) {
|
if (annotation instanceof Directive) {
|
||||||
return new DirectiveMetadata(type, annotation);
|
var resolvedInjectables = null;
|
||||||
|
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
||||||
|
resolvedInjectables = Injector.resolve(annotation.injectables);
|
||||||
|
}
|
||||||
|
return new DirectiveMetadata(type, annotation, resolvedInjectables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {Key, Injector, Injectable} from 'angular2/di'
|
import {Key, Injector, Injectable, ResolvedBinding} from 'angular2/di'
|
||||||
import {Compiler} from './compiler';
|
import {Compiler} from './compiler';
|
||||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||||
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
|
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {List} from 'angular2/src/facade/collection';
|
||||||
import {Promise} from 'angular2/src/facade/async';
|
import {Promise} from 'angular2/src/facade/async';
|
||||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||||
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||||
@ -34,15 +35,15 @@ export class DynamicComponentLoader {
|
|||||||
loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
||||||
this._assertTypeIsComponent(type);
|
this._assertTypeIsComponent(type);
|
||||||
|
|
||||||
var annotation = this._directiveMetadataReader.read(type).annotation;
|
var directiveMetadata = this._directiveMetadataReader.read(type);
|
||||||
|
|
||||||
var inj = this._componentAppInjector(location, injector, annotation.injectables);
|
var inj = this._componentAppInjector(location, injector, directiveMetadata.resolvedInjectables);
|
||||||
|
|
||||||
var hostEi = location.elementInjector;
|
var hostEi = location.elementInjector;
|
||||||
var hostView = location.hostView;
|
var hostView = location.hostView;
|
||||||
|
|
||||||
return this._compiler.compile(type).then(componentProtoView => {
|
return this._compiler.compile(type).then(componentProtoView => {
|
||||||
var context = hostEi.dynamicallyCreateComponent(type, annotation, inj);
|
var context = hostEi.dynamicallyCreateComponent(type, directiveMetadata.annotation, inj);
|
||||||
var componentView = this._instantiateAndHydrateView(componentProtoView, injector, hostEi, context);
|
var componentView = this._instantiateAndHydrateView(componentProtoView, injector, hostEi, context);
|
||||||
|
|
||||||
//TODO(vsavkin): do not use component child views as we need to clear the dynamically created views
|
//TODO(vsavkin): do not use component child views as we need to clear the dynamically created views
|
||||||
@ -78,9 +79,9 @@ export class DynamicComponentLoader {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_componentAppInjector(location, injector, services) {
|
_componentAppInjector(location, injector:Injector, resolvedBindings:List<ResolvedBinding>) {
|
||||||
var inj = isPresent(injector) ? injector : location.injector;
|
var inj = isPresent(injector) ? injector : location.injector;
|
||||||
return isPresent(services) ? inj.resolveAndCreateChild(services) : inj;
|
return isPresent(resolvedBindings) ? inj.createChildFromResolved(resolvedBindings) : inj;
|
||||||
}
|
}
|
||||||
|
|
||||||
_instantiateAndHydrateView(protoView, injector, hostElementInjector, context) {
|
_instantiateAndHydrateView(protoView, injector, hostElementInjector, context) {
|
||||||
|
@ -7,7 +7,7 @@ import {EventEmitter, PropertySetter, Attribute, Query} from 'angular2/src/core/
|
|||||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||||
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||||
import {Directive, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
|
import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
|
||||||
import {BindingPropagationConfig} from 'angular2/change_detection';
|
import {BindingPropagationConfig} from 'angular2/change_detection';
|
||||||
import {QueryList} from './query_list';
|
import {QueryList} from './query_list';
|
||||||
|
|
||||||
@ -283,6 +283,7 @@ export class DirectiveBinding extends ResolvedBinding {
|
|||||||
callOnChange:boolean;
|
callOnChange:boolean;
|
||||||
callOnAllChangesDone:boolean;
|
callOnAllChangesDone:boolean;
|
||||||
annotation:Directive;
|
annotation:Directive;
|
||||||
|
resolvedInjectables:List<ResolvedBinding>;
|
||||||
|
|
||||||
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
|
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
|
||||||
super(key, factory, dependencies, providedAsPromise);
|
super(key, factory, dependencies, providedAsPromise);
|
||||||
@ -290,6 +291,9 @@ export class DirectiveBinding extends ResolvedBinding {
|
|||||||
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
||||||
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
|
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
|
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
||||||
|
this.resolvedInjectables = Injector.resolve(annotation.injectables);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {
|
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {
|
||||||
|
4
modules/angular2/src/core/compiler/view.js
vendored
4
modules/angular2/src/core/compiler/view.js
vendored
@ -151,9 +151,9 @@ export class AppView {
|
|||||||
|
|
||||||
// shadowDomAppInjector
|
// shadowDomAppInjector
|
||||||
if (isPresent(componentDirective)) {
|
if (isPresent(componentDirective)) {
|
||||||
var injectables = componentDirective.annotation.injectables;
|
var injectables = componentDirective.resolvedInjectables;
|
||||||
if (isPresent(injectables))
|
if (isPresent(injectables))
|
||||||
shadowDomAppInjector = appInjector.resolveAndCreateChild(injectables);
|
shadowDomAppInjector = appInjector.createChildFromResolved(injectables);
|
||||||
else {
|
else {
|
||||||
shadowDomAppInjector = appInjector;
|
shadowDomAppInjector = appInjector;
|
||||||
}
|
}
|
||||||
|
6
modules/angular2/src/di/injector.js
vendored
6
modules/angular2/src/di/injector.js
vendored
@ -267,14 +267,12 @@ class _AsyncInjectorStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createListOfBindings(flattenBindings):List {
|
function _createListOfBindings(flattenedBindings):List {
|
||||||
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
|
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
|
||||||
MapWrapper.forEach(flattenBindings, (v, keyId) => bindings[keyId] = v);
|
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _flattenBindings(bindings:List, res:Map) {
|
function _flattenBindings(bindings:List, res:Map) {
|
||||||
ListWrapper.forEach(bindings, function (b) {
|
ListWrapper.forEach(bindings, function (b) {
|
||||||
if (b instanceof ResolvedBinding) {
|
if (b instanceof ResolvedBinding) {
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||||
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
|
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||||
|
import {Injectable, Injector} from 'angular2/di';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
class SomeInjectable {}
|
||||||
|
|
||||||
@Decorator({selector: 'someDecorator'})
|
@Decorator({selector: 'someDecorator'})
|
||||||
class SomeDecorator {}
|
class SomeDecorator {}
|
||||||
|
|
||||||
@Component({selector: 'someComponent'})
|
@Component({selector: 'someComponent', injectables: [SomeInjectable]})
|
||||||
class SomeComponent {}
|
class SomeComponent {}
|
||||||
|
|
||||||
@Viewport({selector: 'someViewport'})
|
@Viewport({selector: 'someViewport'})
|
||||||
@ -27,19 +32,30 @@ export function main() {
|
|||||||
it('should read out the Decorator annotation', () => {
|
it('should read out the Decorator annotation', () => {
|
||||||
var directiveMetadata = reader.read(SomeDecorator);
|
var directiveMetadata = reader.read(SomeDecorator);
|
||||||
expect(directiveMetadata).toEqual(
|
expect(directiveMetadata).toEqual(
|
||||||
new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'})));
|
new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'}), null));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read out the Viewport annotation', () => {
|
it('should read out the Viewport annotation', () => {
|
||||||
var directiveMetadata = reader.read(SomeViewport);
|
var directiveMetadata = reader.read(SomeViewport);
|
||||||
expect(directiveMetadata).toEqual(
|
expect(directiveMetadata).toEqual(
|
||||||
new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'})));
|
new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'}), null));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read out the Component annotation', () => {
|
it('should read out the Component annotation', () => {
|
||||||
var directiveMetadata = reader.read(SomeComponent);
|
var m = reader.read(SomeComponent);
|
||||||
expect(directiveMetadata).toEqual(
|
// For some reason `toEqual` fails to compare ResolvedBinding objects.
|
||||||
new DirectiveMetadata(SomeComponent, new Component({selector: 'someComponent'})));
|
// Have to decompose and compare.
|
||||||
|
expect(m.type).toEqual(SomeComponent);
|
||||||
|
expect(m.annotation)
|
||||||
|
.toEqual(new Component({selector: 'someComponent', injectables: [SomeInjectable]}));
|
||||||
|
var resolvedList = ListWrapper.reduce(m.resolvedInjectables, function(prev, elem) {
|
||||||
|
if (isPresent(elem)) {
|
||||||
|
ListWrapper.push(prev, elem);
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, []);
|
||||||
|
expect(resolvedList.length).toBe(1);
|
||||||
|
expect(resolvedList[0].key.token).toBe(SomeInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if not matching annotation is found', () => {
|
it('should throw if not matching annotation is found', () => {
|
||||||
|
13
modules/angular2/test/di/injector_spec.js
vendored
13
modules/angular2/test/di/injector_spec.js
vendored
@ -1,5 +1,6 @@
|
|||||||
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||||
import {Injector, Inject, InjectLazy, Optional, bind} from 'angular2/di';
|
import {Injector, Inject, InjectLazy, Optional, bind, ResolvedBinding} from 'angular2/di';
|
||||||
|
|
||||||
class Engine {
|
class Engine {
|
||||||
}
|
}
|
||||||
@ -364,5 +365,15 @@ export function main() {
|
|||||||
expect(e1).toBe(e2);
|
expect(e1).toBe(e2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('resolve', function() {
|
||||||
|
it('should resolve and flatten', function() {
|
||||||
|
var bindings = Injector.resolve([Engine, [BrokenEngine]]);
|
||||||
|
bindings.forEach(function(b) {
|
||||||
|
if (isBlank(b)) return; // the result is a sparse array
|
||||||
|
expect(b instanceof ResolvedBinding).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user