feat(view_pool): adds a view pool of dehydrated views per protoview.
This commit is contained in:
parent
617206bd1c
commit
7bf5ab8f43
@ -39,14 +39,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
this.formatters = formatters;
|
this.formatters = formatters;
|
||||||
|
|
||||||
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||||
ListWrapper.fill(this.values, uninitialized);
|
|
||||||
|
|
||||||
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||||
|
|
||||||
this.protos = protoRecords;
|
this.protos = protoRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
setContext(context:any) {
|
setContext(context:any) {
|
||||||
|
ListWrapper.fill(this.values, uninitialized);
|
||||||
this.values[0] = context;
|
this.values[0] = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
modules/angular2/src/core/compiler/view.js
vendored
23
modules/angular2/src/core/compiler/view.js
vendored
@ -15,12 +15,17 @@ import {ViewPort} from './viewport';
|
|||||||
import {Content} from './shadow_dom_emulation/content_tag';
|
import {Content} from './shadow_dom_emulation/content_tag';
|
||||||
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
|
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
|
||||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||||
|
import {ViewPool} from './view_pool';
|
||||||
|
|
||||||
const NG_BINDING_CLASS = 'ng-binding';
|
const NG_BINDING_CLASS = 'ng-binding';
|
||||||
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
||||||
// TODO(tbosch): Cannot use `const` because of Dart.
|
// TODO(tbosch): Cannot use `const` because of Dart.
|
||||||
var NO_FORMATTERS = MapWrapper.create();
|
var NO_FORMATTERS = MapWrapper.create();
|
||||||
|
|
||||||
|
// TODO(rado): make this configurable/smarter.
|
||||||
|
var VIEW_POOL_CAPACITY = 10000;
|
||||||
|
var VIEW_POOL_PREFILL = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
||||||
*/
|
*/
|
||||||
@ -268,6 +273,7 @@ export class ProtoView {
|
|||||||
rootBindingOffset:int;
|
rootBindingOffset:int;
|
||||||
isTemplateElement:boolean;
|
isTemplateElement:boolean;
|
||||||
shadowDomStrategy: ShadowDomStrategy;
|
shadowDomStrategy: ShadowDomStrategy;
|
||||||
|
_viewPool: ViewPool;
|
||||||
constructor(
|
constructor(
|
||||||
template:Element,
|
template:Element,
|
||||||
protoChangeDetector:ProtoChangeDetector,
|
protoChangeDetector:ProtoChangeDetector,
|
||||||
@ -284,10 +290,23 @@ export class ProtoView {
|
|||||||
? 1 : 0;
|
? 1 : 0;
|
||||||
this.isTemplateElement = this.element instanceof TemplateElement;
|
this.isTemplateElement = this.element instanceof TemplateElement;
|
||||||
this.shadowDomStrategy = shadowDomStrategy;
|
this.shadowDomStrategy = shadowDomStrategy;
|
||||||
|
this._viewPool = new ViewPool(VIEW_POOL_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
||||||
instantiate(hostElementInjector: ElementInjector):View {
|
instantiate(hostElementInjector: ElementInjector):View {
|
||||||
|
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector);
|
||||||
|
var view = this._viewPool.pop();
|
||||||
|
return isPresent(view) ? view : this._instantiate(hostElementInjector);
|
||||||
|
}
|
||||||
|
|
||||||
|
_preFillPool(hostElementInjector: ElementInjector) {
|
||||||
|
for (var i = 0; i < VIEW_POOL_PREFILL; i++) {
|
||||||
|
this._viewPool.push(this._instantiate(hostElementInjector));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_instantiate(hostElementInjector: ElementInjector): View {
|
||||||
var rootElementClone = this.instantiateInPlace ? this.element : DOM.clone(this.element);
|
var rootElementClone = this.instantiateInPlace ? this.element : DOM.clone(this.element);
|
||||||
var elementsWithBindingsDynamic;
|
var elementsWithBindingsDynamic;
|
||||||
if (this.isTemplateElement) {
|
if (this.isTemplateElement) {
|
||||||
@ -409,6 +428,10 @@ export class ProtoView {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
returnToPool(view: View) {
|
||||||
|
this._viewPool.push(view);
|
||||||
|
}
|
||||||
|
|
||||||
static _addNativeEventListener(element: Element, eventName: string, expr: AST, view: View) {
|
static _addNativeEventListener(element: Element, eventName: string, expr: AST, view: View) {
|
||||||
var locals = MapWrapper.create();
|
var locals = MapWrapper.create();
|
||||||
var innerCallback = ProtoView.buildInnerCallback(expr, view, locals);
|
var innerCallback = ProtoView.buildInnerCallback(expr, view, locals);
|
||||||
|
26
modules/angular2/src/core/compiler/view_pool.js
vendored
Normal file
26
modules/angular2/src/core/compiler/view_pool.js
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import {ListWrapper, MapWrapper, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||||
|
import {View} from './view';
|
||||||
|
|
||||||
|
export class ViewPool {
|
||||||
|
_views: List<View>;
|
||||||
|
_capacity: number;
|
||||||
|
constructor(capacity: number) {
|
||||||
|
this._views = [];
|
||||||
|
this._capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
pop(): View {
|
||||||
|
return ListWrapper.isEmpty(this._views) ? null : ListWrapper.removeLast(this._views);
|
||||||
|
}
|
||||||
|
|
||||||
|
push(view: View) {
|
||||||
|
if (this._views.length < this._capacity) {
|
||||||
|
ListWrapper.push(this._views, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
length() {
|
||||||
|
return this._views.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -92,6 +92,8 @@ export class ViewPort {
|
|||||||
if (atIndex == -1) atIndex = this._views.length - 1;
|
if (atIndex == -1) atIndex = this._views.length - 1;
|
||||||
var view = this.detach(atIndex);
|
var view = this.detach(atIndex);
|
||||||
view.dehydrate();
|
view.dehydrate();
|
||||||
|
// TODO(rado): this needs to be delayed until after any pending animations.
|
||||||
|
this.defaultProtoView.returnToPool(view);
|
||||||
// view is intentionally not returned to the client.
|
// view is intentionally not returned to the client.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +110,7 @@ class ListWrapper {
|
|||||||
list.remove(items[i]);
|
list.remove(items[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static removeLast(List list) => list.removeLast();
|
||||||
static bool remove(List list, item) => list.remove(item);
|
static bool remove(List list, item) => list.remove(item);
|
||||||
static void clear(List l) {
|
static void clear(List l) {
|
||||||
l.clear();
|
l.clear();
|
||||||
|
@ -149,6 +149,9 @@ export class ListWrapper {
|
|||||||
list.splice(index, 1);
|
list.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static removeLast(list:List) {
|
||||||
|
return list.pop();
|
||||||
|
}
|
||||||
static remove(list, el): boolean {
|
static remove(list, el): boolean {
|
||||||
var index = list.indexOf(el);
|
var index = list.indexOf(el);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
|
46
modules/angular2/test/core/compiler/view_pool_spec.js
vendored
Normal file
46
modules/angular2/test/core/compiler/view_pool_spec.js
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {View} from 'angular2/src/core/compiler/view';
|
||||||
|
import {ViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||||
|
import {proxy, IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(View)
|
||||||
|
class FakeView {
|
||||||
|
noSuchMethod(i) {
|
||||||
|
super.noSuchMethod(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('ViewPool', () => {
|
||||||
|
var viewPool, capacity = 3;
|
||||||
|
beforeEach(() => {
|
||||||
|
viewPool = new ViewPool(capacity);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return null when there are no views', () => {
|
||||||
|
expect(viewPool.pop()).toBeNull();
|
||||||
|
expect(viewPool.length()).toBe(0);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support storing and retrieving a view', () => {
|
||||||
|
var view = new FakeView();
|
||||||
|
viewPool.push(view);
|
||||||
|
expect(viewPool.length()).toBe(1);
|
||||||
|
|
||||||
|
expect(viewPool.pop()).toBe(view);
|
||||||
|
expect(viewPool.length()).toBe(0);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not store more views that its capacity', () => {
|
||||||
|
for (var i = 0; i < capacity * 2; i++) viewPool.push(new FakeView());
|
||||||
|
expect(viewPool.length()).toBe(capacity);
|
||||||
|
|
||||||
|
for (var i = 0; i < capacity; i++) {
|
||||||
|
expect(viewPool.pop()).not.toBe(null);
|
||||||
|
}
|
||||||
|
expect(viewPool.pop()).toBeNull();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
15
modules/angular2/test/core/compiler/view_spec.js
vendored
15
modules/angular2/test/core/compiler/view_spec.js
vendored
@ -31,6 +31,13 @@ class FakeViewPort {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(View)
|
||||||
|
class FakeView {
|
||||||
|
noSuchMethod(i) {
|
||||||
|
super.noSuchMethod(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('view', function() {
|
describe('view', function() {
|
||||||
@ -68,6 +75,14 @@ export function main() {
|
|||||||
view.dehydrate();
|
view.dehydrate();
|
||||||
expect(view.hydrated()).toBe(false);
|
expect(view.hydrated()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use the view pool to reuse views', () => {
|
||||||
|
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(), null);
|
||||||
|
var fakeView = new FakeView();
|
||||||
|
pv.returnToPool(fakeView);
|
||||||
|
|
||||||
|
expect(pv.instantiate(null)).toBe(fakeView);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with locals', function() {
|
describe('with locals', function() {
|
||||||
|
@ -43,7 +43,7 @@ function setupReflector() {
|
|||||||
},
|
},
|
||||||
template: new TemplateConfig({
|
template: new TemplateConfig({
|
||||||
directives: [TreeComponent, NgIf],
|
directives: [TreeComponent, NgIf],
|
||||||
inline: `<span>{{data.value}}<span template='ng-if data.right != null'><tree [data]='data.right'></tree></span><span template='ng-if data.left != null'><tree [data]='data.left'></tree></span></span>`
|
inline: `<span> {{data.value}} <span template='ng-if data.right != null'><tree [data]='data.right'></tree></span><span template='ng-if data.left != null'><tree [data]='data.left'></tree></span></span>`
|
||||||
})
|
})
|
||||||
})]
|
})]
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user