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 {List} from 'angular2/src/facade/collection';
|
||||
import {Directive} from 'angular2/src/core/annotations/annotations'
|
||||
import {ResolvedBinding} from 'angular2/di';
|
||||
|
||||
/**
|
||||
* Combination of a type with the Directive annotation
|
||||
|
@ -7,9 +9,11 @@ import {Directive} from 'angular2/src/core/annotations/annotations'
|
|||
export class DirectiveMetadata {
|
||||
type:Type;
|
||||
annotation:Directive;
|
||||
resolvedInjectables:List<ResolvedBinding>;
|
||||
|
||||
constructor(type:Type, annotation:Directive) {
|
||||
constructor(type:Type, annotation:Directive, resolvedInjectables:List<ResolvedBinding>) {
|
||||
this.annotation = annotation;
|
||||
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 {Directive} from '../annotations/annotations';
|
||||
import {Directive, Component} from '../annotations/annotations';
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
|
@ -13,7 +13,11 @@ export class DirectiveMetadataReader {
|
|||
var annotation = annotations[i];
|
||||
|
||||
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 {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
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 {Component} from 'angular2/src/core/annotations/annotations';
|
||||
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> {
|
||||
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 hostView = location.hostView;
|
||||
|
||||
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);
|
||||
|
||||
//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;
|
||||
return isPresent(services) ? inj.resolveAndCreateChild(services) : inj;
|
||||
return isPresent(resolvedBindings) ? inj.createChildFromResolved(resolvedBindings) : inj;
|
||||
}
|
||||
|
||||
_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 {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
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 {QueryList} from './query_list';
|
||||
|
||||
|
@ -283,6 +283,7 @@ export class DirectiveBinding extends ResolvedBinding {
|
|||
callOnChange:boolean;
|
||||
callOnAllChangesDone:boolean;
|
||||
annotation:Directive;
|
||||
resolvedInjectables:List<ResolvedBinding>;
|
||||
|
||||
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
|
||||
super(key, factory, dependencies, providedAsPromise);
|
||||
|
@ -290,6 +291,9 @@ export class DirectiveBinding extends ResolvedBinding {
|
|||
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
||||
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
|
||||
this.annotation = annotation;
|
||||
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
||||
this.resolvedInjectables = Injector.resolve(annotation.injectables);
|
||||
}
|
||||
}
|
||||
|
||||
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {
|
||||
|
|
|
@ -151,9 +151,9 @@ export class AppView {
|
|||
|
||||
// shadowDomAppInjector
|
||||
if (isPresent(componentDirective)) {
|
||||
var injectables = componentDirective.annotation.injectables;
|
||||
var injectables = componentDirective.resolvedInjectables;
|
||||
if (isPresent(injectables))
|
||||
shadowDomAppInjector = appInjector.resolveAndCreateChild(injectables);
|
||||
shadowDomAppInjector = appInjector.createChildFromResolved(injectables);
|
||||
else {
|
||||
shadowDomAppInjector = appInjector;
|
||||
}
|
||||
|
|
|
@ -267,14 +267,12 @@ class _AsyncInjectorStrategy {
|
|||
}
|
||||
}
|
||||
|
||||
function _createListOfBindings(flattenBindings):List {
|
||||
function _createListOfBindings(flattenedBindings):List {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function _flattenBindings(bindings:List, res:Map) {
|
||||
ListWrapper.forEach(bindings, function (b) {
|
||||
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 {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {Injectable, Injector} from 'angular2/di';
|
||||
|
||||
@Injectable()
|
||||
class SomeInjectable {}
|
||||
|
||||
@Decorator({selector: 'someDecorator'})
|
||||
class SomeDecorator {}
|
||||
|
||||
@Component({selector: 'someComponent'})
|
||||
@Component({selector: 'someComponent', injectables: [SomeInjectable]})
|
||||
class SomeComponent {}
|
||||
|
||||
@Viewport({selector: 'someViewport'})
|
||||
|
@ -27,19 +32,30 @@ export function main() {
|
|||
it('should read out the Decorator annotation', () => {
|
||||
var directiveMetadata = reader.read(SomeDecorator);
|
||||
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', () => {
|
||||
var directiveMetadata = reader.read(SomeViewport);
|
||||
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', () => {
|
||||
var directiveMetadata = reader.read(SomeComponent);
|
||||
expect(directiveMetadata).toEqual(
|
||||
new DirectiveMetadata(SomeComponent, new Component({selector: 'someComponent'})));
|
||||
var m = reader.read(SomeComponent);
|
||||
// For some reason `toEqual` fails to compare ResolvedBinding objects.
|
||||
// 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', () => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
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 {
|
||||
}
|
||||
|
@ -364,5 +365,15 @@ export function main() {
|
|||
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…
Reference in New Issue