refactor(render): don’t store DOM nodes but store strings for big ProtoViews.
Also inserts comment nodes before/after projected nodes so that text nodes don’t get merged when we serialize/deserialize them. Closes #3356 First part of #3364
This commit is contained in:
@ -12,6 +12,8 @@ export const EVENT_TARGET_SEPARATOR = ':';
|
||||
export const NG_CONTENT_ELEMENT_NAME = 'ng-content';
|
||||
export const NG_SHADOW_ROOT_ELEMENT_NAME = 'shadow-root';
|
||||
|
||||
const MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE = 20;
|
||||
|
||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||
var DASH_CASE_REGEXP = /-([a-z])/g;
|
||||
|
||||
@ -57,8 +59,7 @@ export class ClonedProtoView {
|
||||
|
||||
export function cloneAndQueryProtoView(pv: DomProtoView, importIntoDocument: boolean):
|
||||
ClonedProtoView {
|
||||
var templateContent = importIntoDocument ? DOM.importIntoDoc(DOM.content(pv.rootElement)) :
|
||||
DOM.clone(DOM.content(pv.rootElement));
|
||||
var templateContent = pv.cloneableTemplate.clone(importIntoDocument);
|
||||
|
||||
var boundElements = queryBoundElements(templateContent, pv.isSingleElementFragment);
|
||||
var boundTextNodes = queryBoundTextNodes(templateContent, pv.rootTextNodeIndices, boundElements,
|
||||
@ -140,3 +141,45 @@ export function prependAll(parentNode: Node, nodes: Node[]) {
|
||||
lastInsertedNode = node;
|
||||
});
|
||||
}
|
||||
|
||||
export interface CloneableTemplate { clone(importIntoDoc: boolean): Node; }
|
||||
|
||||
export class SerializedCloneableTemplate implements CloneableTemplate {
|
||||
templateString: string;
|
||||
constructor(templateRoot: Element) { this.templateString = DOM.getInnerHTML(templateRoot); }
|
||||
clone(importIntoDoc: boolean): Node {
|
||||
var result = DOM.content(DOM.createTemplate(this.templateString));
|
||||
if (importIntoDoc) {
|
||||
result = DOM.adoptNode(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class ReferenceCloneableTemplate implements CloneableTemplate {
|
||||
constructor(public templateRoot: Element) {}
|
||||
clone(importIntoDoc: boolean): Node {
|
||||
if (importIntoDoc) {
|
||||
return DOM.importIntoDoc(DOM.content(this.templateRoot));
|
||||
} else {
|
||||
return DOM.clone(DOM.content(this.templateRoot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function prepareTemplateForClone(templateRoot: Element): CloneableTemplate {
|
||||
var root = DOM.content(templateRoot);
|
||||
var elementCount = DOM.querySelectorAll(root, '*').length;
|
||||
var firstChild = DOM.firstChild(root);
|
||||
var forceSerialize =
|
||||
isPresent(firstChild) && DOM.isCommentNode(firstChild) ? DOM.nodeValue(firstChild) : null;
|
||||
if (forceSerialize == 'nocache') {
|
||||
return new SerializedCloneableTemplate(templateRoot);
|
||||
} else if (forceSerialize == 'cache') {
|
||||
return new ReferenceCloneableTemplate(templateRoot);
|
||||
} else if (elementCount > MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE) {
|
||||
return new SerializedCloneableTemplate(templateRoot);
|
||||
} else {
|
||||
return new ReferenceCloneableTemplate(templateRoot);
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ import {RenderProtoViewRef, ViewType, ViewEncapsulation} from '../../api';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {prepareTemplateForClone, CloneableTemplate} from '../util';
|
||||
|
||||
export function resolveInternalDomProtoView(protoViewRef: RenderProtoViewRef): DomProtoView {
|
||||
return (<DomProtoViewRef>protoViewRef)._protoView;
|
||||
}
|
||||
@ -25,12 +27,12 @@ export class DomProtoView {
|
||||
var isSingleElementFragment = fragmentsRootNodeCount.length === 1 &&
|
||||
fragmentsRootNodeCount[0] === 1 &&
|
||||
DOM.isElementNode(DOM.firstChild(DOM.content(rootElement)));
|
||||
return new DomProtoView(type, rootElement, viewEncapsulation, elementBinders, hostAttributes,
|
||||
rootTextNodeIndices, boundTextNodeCount, fragmentsRootNodeCount,
|
||||
isSingleElementFragment);
|
||||
return new DomProtoView(type, prepareTemplateForClone(rootElement), viewEncapsulation,
|
||||
elementBinders, hostAttributes, rootTextNodeIndices, boundTextNodeCount,
|
||||
fragmentsRootNodeCount, isSingleElementFragment);
|
||||
}
|
||||
|
||||
constructor(public type: ViewType, public rootElement: Element,
|
||||
constructor(public type: ViewType, public cloneableTemplate: CloneableTemplate,
|
||||
public encapsulation: ViewEncapsulation,
|
||||
public elementBinders: List<DomElementBinder>,
|
||||
public hostAttributes: Map<string, string>, public rootTextNodeIndices: number[],
|
||||
|
@ -251,6 +251,7 @@ function appendComponentNodesToHost(hostProtoView: ClonedProtoView, binderIdx: n
|
||||
|
||||
function projectMatchingNodes(selector: string, contentElement: Element, nodes: Node[]): Node[] {
|
||||
var remaining = [];
|
||||
DOM.insertBefore(contentElement, DOM.createComment('['));
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
var matches = false;
|
||||
@ -265,6 +266,7 @@ function projectMatchingNodes(selector: string, contentElement: Element, nodes:
|
||||
remaining.push(node);
|
||||
}
|
||||
}
|
||||
DOM.insertBefore(contentElement, DOM.createComment(']'));
|
||||
DOM.remove(contentElement);
|
||||
return remaining;
|
||||
}
|
||||
|
Reference in New Issue
Block a user