Skip to content

Commit 950f2ce

Browse files
committedApr 17, 2017
dont update static subtrees, even with a noop
1 parent 92b49ee commit 950f2ce

File tree

11 files changed

+287
-70
lines changed

11 files changed

+287
-70
lines changed
 

‎src/generators/Generator.js

+39-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default class Generator {
7373
const { code, helpers } = this;
7474
const { contextDependencies, contexts, indexes } = block;
7575

76-
let scope = annotateWithScopes( expression );
76+
let scope = annotateWithScopes( expression ); // TODO this already happens in findDependencies
7777
let lexicalDepth = 0;
7878

7979
const self = this;
@@ -164,6 +164,44 @@ export default class Generator {
164164
return expression._contextualised;
165165
}
166166

167+
findDependencies ( block, expression, isEventHandler ) {
168+
const dependencies = [];
169+
170+
const { contextDependencies, contexts } = block;
171+
172+
let scope = annotateWithScopes( expression );
173+
174+
walk( expression, {
175+
enter ( node, parent ) {
176+
if ( node._scope ) {
177+
scope = node._scope;
178+
return;
179+
}
180+
181+
if ( isReference( node, parent ) ) {
182+
const { name } = flattenReference( node );
183+
if ( scope.has( name ) ) return;
184+
185+
if ( name === 'event' && isEventHandler ) {
186+
// noop
187+
} else if ( contexts.has( name ) ) {
188+
dependencies.push( ...contextDependencies.get( name ) );
189+
} else {
190+
dependencies.push( name );
191+
}
192+
193+
this.skip();
194+
}
195+
},
196+
197+
leave ( node ) {
198+
if ( node._scope ) scope = scope.parent;
199+
}
200+
});
201+
202+
return dependencies;
203+
}
204+
167205
generate ( result, options, { name, format } ) {
168206
if ( this.imports.length ) {
169207
const statements = [];

‎src/generators/dom/Block.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export default class Block {
3434
// unique names
3535
this.component = this.getUniqueName( 'component' );
3636
this.target = this.getUniqueName( 'target' );
37+
38+
this.hasUpdateMethod = false; // determined later
3739
}
3840

3941
addDependencies ( dependencies ) {
@@ -72,6 +74,10 @@ export default class Block {
7274
this.addElement( name, renderStatement, parentNode, true );
7375
}
7476

77+
findDependencies ( expression, isEventHandler ) {
78+
return this.generator.findDependencies( this, expression, isEventHandler );
79+
}
80+
7581
mount ( name, parentNode ) {
7682
if ( parentNode ) {
7783
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${name}, ${parentNode} );` );
@@ -115,15 +121,17 @@ export default class Block {
115121
` );
116122
}
117123

118-
if ( this.builders.update.isEmpty() ) {
119-
properties.addBlock( `update: ${this.generator.helper( 'noop' )},` );
120-
} else {
121-
if ( this._tmp ) this.builders.update.addBlockAtStart( `var ${this._tmp};` );
122-
properties.addBlock( deindent`
123-
update: function ( changed, ${this.params.join( ', ' )} ) {
124-
${this.builders.update}
125-
},
126-
` );
124+
if ( this.hasUpdateMethod ) {
125+
if ( this.builders.update.isEmpty() ) {
126+
properties.addBlock( `update: ${this.generator.helper( 'noop' )},` );
127+
} else {
128+
if ( this._tmp ) this.builders.update.addBlockAtStart( `var ${this._tmp};` );
129+
properties.addBlock( deindent`
130+
update: function ( changed, ${this.params.join( ', ' )} ) {
131+
${this.builders.update}
132+
},
133+
` );
134+
}
127135
}
128136

129137
if ( this.builders.destroy.isEmpty() ) {

‎src/generators/dom/index.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,9 @@ export default function dom ( parsed, source, options ) {
107107
builders._set.addLine( `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )` );
108108
}
109109

110-
// TODO is the `if` necessary?
111-
builders._set.addBlock( deindent`
112-
${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );
113-
if ( this._fragment ) this._fragment.update( newState, this._state );
114-
${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );
115-
` );
110+
builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );` );
111+
if ( block.hasUpdateMethod ) builders._set.addLine( `if ( this._fragment ) this._fragment.update( newState, this._state );` ); // TODO is the condition necessary?
112+
builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );` );
116113

117114
if ( hasJs ) {
118115
builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );

‎src/generators/dom/preprocess.js

+39-9
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,29 @@ function isElseIf ( node ) {
66

77
const preprocessors = {
88
MustacheTag: ( generator, block, node ) => {
9-
const { dependencies } = block.contextualise( node.expression );
9+
const dependencies = block.findDependencies( node.expression );
1010
block.addDependencies( dependencies );
1111
},
1212

1313
IfBlock: ( generator, block, node ) => {
14+
const blocks = [];
15+
let dynamic = false;
16+
1417
function attachBlocks ( node ) {
15-
const { dependencies } = block.contextualise( node.expression );
18+
const dependencies = block.findDependencies( node.expression );
1619
block.addDependencies( dependencies );
1720

1821
node._block = block.child({
1922
name: generator.getUniqueName( `create_if_block` )
2023
});
2124

22-
generator.blocks.push( node._block );
25+
blocks.push( node._block );
2326
preprocessChildren( generator, node._block, node.children );
24-
block.addDependencies( node._block.dependencies );
27+
28+
if ( node._block.dependencies.size > 0 ) {
29+
dynamic = true;
30+
block.addDependencies( node._block.dependencies );
31+
}
2532

2633
if ( isElseIf( node.else ) ) {
2734
attachBlocks( node.else.children[0] );
@@ -30,17 +37,27 @@ const preprocessors = {
3037
name: generator.getUniqueName( `create_if_block` )
3138
});
3239

33-
generator.blocks.push( node.else._block );
40+
blocks.push( node.else._block );
3441
preprocessChildren( generator, node.else._block, node.else.children );
35-
block.addDependencies( node.else._block.dependencies );
42+
43+
if ( node.else._block.dependencies.size > 0 ) {
44+
dynamic = true;
45+
block.addDependencies( node.else._block.dependencies );
46+
}
3647
}
3748
}
3849

3950
attachBlocks( node );
51+
52+
blocks.forEach( block => {
53+
block.hasUpdateMethod = dynamic;
54+
});
55+
56+
generator.blocks.push( ...blocks );
4057
},
4158

4259
EachBlock: ( generator, block, node ) => {
43-
const { dependencies } = block.contextualise( node.expression );
60+
const dependencies = block.findDependencies( node.expression );
4461
block.addDependencies( dependencies );
4562

4663
const indexNames = new Map( block.indexNames );
@@ -82,6 +99,7 @@ const preprocessors = {
8299
generator.blocks.push( node._block );
83100
preprocessChildren( generator, node._block, node.children );
84101
block.addDependencies( node._block.dependencies );
102+
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
85103

86104
if ( node.else ) {
87105
node.else._block = block.child({
@@ -90,6 +108,7 @@ const preprocessors = {
90108

91109
generator.blocks.push( node.else._block );
92110
preprocessChildren( generator, node.else._block, node.else.children );
111+
node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0;
93112
}
94113
},
95114

@@ -98,16 +117,24 @@ const preprocessors = {
98117
if ( attribute.type === 'Attribute' && attribute.value !== true ) {
99118
attribute.value.forEach( chunk => {
100119
if ( chunk.type !== 'Text' ) {
101-
const { dependencies } = block.contextualise( chunk.expression );
120+
const dependencies = block.findDependencies( chunk.expression );
102121
block.addDependencies( dependencies );
103122
}
104123
});
105124
}
106125

107126
else if ( attribute.type === 'Binding' ) {
108-
const { dependencies } = block.contextualise( attribute.value );
127+
const dependencies = block.findDependencies( attribute.value );
109128
block.addDependencies( dependencies );
110129
}
130+
131+
// else if ( attribute.type === 'EventHandler' ) {
132+
// // TODO is this necessary?
133+
// attribute.expression.arguments.forEach( arg => {
134+
// const dependencies = block.findDependencies( arg );
135+
// block.addDependencies( dependencies );
136+
// });
137+
// }
111138
});
112139

113140
const isComponent = generator.components.has( node.name ) || node.name === ':Self';
@@ -121,6 +148,8 @@ const preprocessors = {
121148

122149
generator.blocks.push( node._block );
123150
preprocessChildren( generator, node._block, node.children );
151+
block.addDependencies( node._block.dependencies );
152+
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
124153
}
125154

126155
else {
@@ -156,6 +185,7 @@ export default function preprocess ( generator, children ) {
156185

157186
generator.blocks.push( block );
158187
preprocessChildren( generator, block, children );
188+
block.hasUpdateMethod = block.dependencies.size > 0;
159189

160190
return block;
161191
}

‎src/generators/dom/visitors/EachBlock.js

+26-9
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
125125
}
126126
` );
127127

128+
const consequent = node._block.hasUpdateMethod ?
129+
deindent`
130+
${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${lookup}[ ${key} ];
131+
${_lookup}[ ${key} ].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
132+
` :
133+
`${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${lookup}[ ${key} ];`;
134+
128135
block.builders.update.addBlock( deindent`
129136
var ${each_block_value} = ${snippet};
130137
var ${_iterations} = [];
@@ -138,8 +145,7 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
138145
var ${key} = ${value}.${node.key};
139146
140147
if ( ${lookup}[ ${key} ] ) {
141-
${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${lookup}[ ${key} ];
142-
${_lookup}[ ${key} ].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
148+
${consequent}
143149
} else {
144150
${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}${node.key ? `, ${key}` : `` } );
145151
}
@@ -192,17 +198,28 @@ function unkeyed ( generator, block, state, node, snippet, { create_each_block,
192198
.join( ' || ' );
193199

194200
if ( condition !== '' ) {
201+
const forLoopBody = node._block.hasUpdateMethod ?
202+
deindent`
203+
if ( ${iterations}[${i}] ) {
204+
${iterations}[${i}].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
205+
} else {
206+
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
207+
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
208+
}
209+
` :
210+
deindent`
211+
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
212+
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
213+
`;
214+
215+
const start = node._block.hasUpdateMethod ? '0' : `${iterations}.length`;
216+
195217
block.builders.update.addBlock( deindent`
196218
var ${each_block_value} = ${snippet};
197219
198220
if ( ${condition} ) {
199-
for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) {
200-
if ( !${iterations}[${i}] ) {
201-
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
202-
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
203-
} else {
204-
${iterations}[${i}].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
205-
}
221+
for ( var ${i} = ${start}; ${i} < ${each_block_value}.length; ${i} += 1 ) {
222+
${forLoopBody}
206223
}
207224
208225
${generator.helper( 'destroyEach' )}( ${iterations}, true, ${each_block_value}.length );

‎src/generators/dom/visitors/IfBlock.js

+32-16
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,32 @@ function isElseIf ( node ) {
55
return node && node.children.length === 1 && node.children[0].type === 'IfBlock';
66
}
77

8-
function getConditionsAndBlocks ( generator, block, state, node ) {
9-
const conditionsAndBlocks = [{
8+
function getBranches ( generator, block, state, node ) {
9+
const branches = [{
1010
condition: block.contextualise( node.expression ).snippet,
11-
block: node._block.name
11+
block: node._block.name,
12+
dynamic: node._block.dependencies.size > 0
1213
}];
1314

1415
visitChildren( generator, block, state, node );
1516

1617
if ( isElseIf( node.else ) ) {
17-
conditionsAndBlocks.push(
18-
...getConditionsAndBlocks( generator, block, state, node.else.children[0] )
18+
branches.push(
19+
...getBranches( generator, block, state, node.else.children[0] )
1920
);
2021
} else {
21-
conditionsAndBlocks.push({
22+
branches.push({
2223
condition: null,
2324
block: node.else ? node.else._block.name : null,
25+
dynamic: node.else ? node.else._block.dependencies.size > 0 : false
2426
});
2527

2628
if ( node.else ) {
2729
visitChildren( generator, block, state, node.else );
2830
}
2931
}
3032

31-
return conditionsAndBlocks;
33+
return branches;
3234
}
3335

3436
function visitChildren ( generator, block, state, node ) {
@@ -48,14 +50,15 @@ export default function visitIfBlock ( generator, block, state, node ) {
4850
const currentBlock = block.getUniqueName( `current_block` );
4951
const _currentBlock = block.getUniqueName( `_current_block` );
5052

51-
const conditionsAndBlocks = getConditionsAndBlocks( generator, block, state, node, generator.getUniqueName( `create_if_block` ) );
53+
const branches = getBranches( generator, block, state, node, generator.getUniqueName( `create_if_block` ) );
54+
const dynamic = branches.some( branch => branch.dynamic );
5255

5356
const anchor = `${name}_anchor`;
5457
block.createAnchor( anchor, state.parentNode );
5558

5659
block.builders.create.addBlock( deindent`
5760
function ${getBlock} ( ${params} ) {
58-
${conditionsAndBlocks.map( ({ condition, block }) => {
61+
${branches.map( ({ condition, block }) => {
5962
return `${condition ? `if ( ${condition} ) ` : ''}return ${block};`;
6063
} ).join( '\n' )}
6164
}
@@ -75,15 +78,28 @@ export default function visitIfBlock ( generator, block, state, node ) {
7578
block.builders.update.addBlock( deindent`
7679
var ${_currentBlock} = ${currentBlock};
7780
${currentBlock} = ${getBlock}( ${params} );
78-
if ( ${_currentBlock} === ${currentBlock} && ${name}) {
79-
${name}.update( changed, ${params} );
80-
} else {
81-
if ( ${name} ) ${name}.destroy( true );
82-
${name} = ${currentBlock} && ${currentBlock}( ${params}, ${block.component} );
83-
if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );
84-
}
8581
` );
8682

83+
if ( dynamic ) {
84+
block.builders.update.addBlock( deindent`
85+
if ( ${_currentBlock} === ${currentBlock} && ${name} ) {
86+
${name}.update( changed, ${params} );
87+
} else {
88+
if ( ${name} ) ${name}.destroy( true );
89+
${name} = ${currentBlock} && ${currentBlock}( ${params}, ${block.component} );
90+
if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );
91+
}
92+
` );
93+
} else {
94+
block.builders.update.addBlock( deindent`
95+
if ( ${_currentBlock} !== ${currentBlock} ) {
96+
if ( ${name} ) ${name}.destroy( true );
97+
${name} = ${currentBlock} && ${currentBlock}( ${params}, ${block.component} );
98+
if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );
99+
}
100+
` );
101+
}
102+
87103
block.builders.destroy.addLine(
88104
`if ( ${name} ) ${name}.destroy( ${isToplevel ? 'detach' : 'false'} );`
89105
);

0 commit comments

Comments
 (0)