Skip to content
This repository was archived by the owner on Apr 4, 2019. It is now read-only.

Commit 4a2362c

Browse files
Tom Dale and Yehuda Katztilde-engineering
Tom Dale and Yehuda Katz
authored andcommitted
Add ability for templates to reuse morphs
Previously, if you wanted to re-render an HTMLBars template, the template would create a brand new clone of the cached fragment and create a new set of morphs. This resulted in unnecessary work, since the static portions of the rendered DOM already exist, and do not need to be re-cloned and re-inserted into DOM. This change begins the process of allowing an HTMLBars template to update the output of a previous render, rather than starting from scratch. It introduces the concept of a (very WIP atm) “result” of a previous render, which is passed back in to subsequent renders. At the moment, the “result” is the fragment and morphs of the previous render. Soon, it will become a more opaque data structure that includes the results of sub-programs.
1 parent df2a2c8 commit 4a2362c

8 files changed

+160
-94
lines changed

packages/htmlbars-compiler/lib/hydration-javascript-compiler.js

+26-14
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,28 @@ prototype.compile = function(opcodes, options) {
3030

3131
processOpcodes(this, opcodes);
3232

33-
var i, l;
33+
var i, l, morphs;
34+
35+
var indent = this.indent + ' ';
36+
3437
if (this.morphs.length) {
35-
var morphs = "";
36-
for (i = 0, l = this.morphs.length; i < l; ++i) {
37-
var morph = this.morphs[i];
38-
morphs += this.indent+' var '+morph[0]+' = '+morph[1]+';\n';
39-
}
40-
this.source.unshift(morphs);
38+
morphs =
39+
indent+'var morphs = env.morphs;\n' +
40+
indent+'if (!morphs) {\n' +
41+
indent+' morphs = new Array(' + this.morphs.length + ');\n';
42+
43+
for (i = 0, l = this.morphs.length; i < l; ++i) {
44+
var morph = this.morphs[i];
45+
morphs += indent+' morphs['+i+'] = '+morph+';\n';
46+
}
47+
48+
morphs += indent+'}\n';
49+
} else {
50+
morphs = indent+'var morphs;\n';
4151
}
4252

53+
this.source.unshift(morphs);
54+
4355
if (this.fragmentProcessing.length) {
4456
var processing = "";
4557
for (i = 0, l = this.fragmentProcessing.length; i < l; ++i) {
@@ -130,7 +142,7 @@ prototype.printSetHook = function(name, index) {
130142
prototype.printBlockHook = function(morphNum, templateId, inverseId) {
131143
this.printHook('block', [
132144
'env',
133-
'morph' + morphNum,
145+
'morphs[' + morphNum + ']',
134146
'context',
135147
this.stack.pop(), // path
136148
this.stack.pop(), // params
@@ -143,7 +155,7 @@ prototype.printBlockHook = function(morphNum, templateId, inverseId) {
143155
prototype.printInlineHook = function(morphNum) {
144156
this.printHook('inline', [
145157
'env',
146-
'morph' + morphNum,
158+
'morphs[' + morphNum + ']',
147159
'context',
148160
this.stack.pop(), // path
149161
this.stack.pop(), // params
@@ -154,7 +166,7 @@ prototype.printInlineHook = function(morphNum) {
154166
prototype.printContentHook = function(morphNum) {
155167
this.printHook('content', [
156168
'env',
157-
'morph' + morphNum,
169+
'morphs[' + morphNum + ']',
158170
'context',
159171
this.stack.pop() // path
160172
]);
@@ -163,7 +175,7 @@ prototype.printContentHook = function(morphNum) {
163175
prototype.printComponentHook = function(morphNum, templateId) {
164176
this.printHook('component', [
165177
'env',
166-
'morph' + morphNum,
178+
'morphs[' + morphNum + ']',
167179
'context',
168180
this.stack.pop(), // path
169181
this.stack.pop(), // attrs
@@ -174,7 +186,7 @@ prototype.printComponentHook = function(morphNum, templateId) {
174186
prototype.printAttributeHook = function(attrMorphNum, elementNum) {
175187
this.printHook('attribute', [
176188
'env',
177-
'attrMorph' + attrMorphNum,
189+
'morphs[' + attrMorphNum + ']',
178190
'element' + elementNum,
179191
this.stack.pop(), // name
180192
this.stack.pop() // value
@@ -202,13 +214,13 @@ prototype.createMorph = function(morphNum, parentPath, startIndex, endIndex, esc
202214
","+(endIndex === null ? "-1" : endIndex)+
203215
(isRoot ? ",contextualElement)" : ")");
204216

205-
this.morphs.push(['morph' + morphNum, morph]);
217+
this.morphs[morphNum] = morph;
206218
};
207219

208220
prototype.createAttrMorph = function(attrMorphNum, elementNum, name, escaped, namespace) {
209221
var morphMethod = escaped ? 'createAttrMorph' : 'createUnsafeAttrMorph';
210222
var morph = "dom."+morphMethod+"(element"+elementNum+", '"+name+(namespace ? "', '"+namespace : '')+"')";
211-
this.morphs.push(['attrMorph' + attrMorphNum, morph]);
223+
this.morphs[attrMorphNum] = morph;
212224
};
213225

214226
prototype.repairClonedNode = function(blankChildTextNodes, isElementChecked) {

packages/htmlbars-compiler/lib/hydration-opcode-compiler.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ function HydrationOpcodeCompiler() {
2828
this.currentDOMChildIndex = 0;
2929
this.morphs = [];
3030
this.morphNum = 0;
31-
this.attrMorphNum = 0;
3231
this.element = null;
3332
this.elementNum = -1;
3433
}
@@ -60,7 +59,6 @@ HydrationOpcodeCompiler.prototype.startProgram = function(program, c, blankChild
6059
this.templateId = 0;
6160
this.currentDOMChildIndex = -1;
6261
this.morphNum = 0;
63-
this.attrMorphNum = 0;
6462

6563
var blockParams = program.blockParams || [];
6664

@@ -194,7 +192,7 @@ HydrationOpcodeCompiler.prototype.attribute = function(attr) {
194192
this.element = null;
195193
}
196194

197-
var attrMorphNum = this.attrMorphNum++;
195+
var attrMorphNum = this.morphNum++;
198196
this.opcode('createAttrMorph', attrMorphNum, this.elementNum, attr.name, escaped, namespace);
199197
this.opcode('printAttributeHook', attrMorphNum, this.elementNum);
200198
};

packages/htmlbars-compiler/lib/template-compiler.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ TemplateCompiler.prototype.endProgram = function(program, programDepth) {
101101
indent+' var dom = env.dom;\n' +
102102
this.getHydrationHooks(indent + ' ', this.hydrationCompiler.hooks) +
103103
indent+' dom.detectNamespace(contextualElement);\n' +
104-
indent+' var fragment;\n' +
105-
indent+' if (env.useFragmentCache && dom.canClone) {\n' +
104+
indent+' var fragment = env.target;\n' +
105+
indent+' if (!fragment && env.useFragmentCache && dom.canClone) {\n' +
106106
indent+' if (this.cachedFragment === null) {\n' +
107107
indent+' fragment = this.build(dom);\n' +
108108
indent+' if (this.hasRendered) {\n' +
@@ -114,11 +114,11 @@ TemplateCompiler.prototype.endProgram = function(program, programDepth) {
114114
indent+' if (this.cachedFragment) {\n' +
115115
indent+' fragment = dom.cloneNode(this.cachedFragment, true);\n' +
116116
indent+' }\n' +
117-
indent+' } else {\n' +
117+
indent+' } else if (!fragment) {\n' +
118118
indent+' fragment = this.build(dom);\n' +
119119
indent+' }\n' +
120120
hydrationProgram +
121-
indent+' return fragment;\n' +
121+
indent+' return { fragment: fragment, morphs: morphs };\n' +
122122
indent+' }\n' +
123123
indent+' };\n' +
124124
indent+'}())';

0 commit comments

Comments
 (0)