Skip to content

Commit fc31d39

Browse files
authored
Merge pull request #490 from sveltejs/gh-472
Only update dynamic subtrees
2 parents aeedb94 + 080afc9 commit fc31d39

File tree

17 files changed

+336
-155
lines changed

17 files changed

+336
-155
lines changed

src/generators/Generator.js

+42-14
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,14 @@ export default class Generator {
6363
}
6464

6565
contextualise ( block, expression, context, isEventHandler ) {
66-
if ( expression._contextualised ) return expression._contextualised;
67-
6866
this.addSourcemapLocations( expression );
6967

7068
const usedContexts = [];
71-
const dependencies = [];
7269

7370
const { code, helpers } = this;
74-
const { contextDependencies, contexts, indexes } = block;
71+
const { contexts, indexes } = block;
7572

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

7976
const self = this;
@@ -110,7 +107,6 @@ export default class Generator {
110107
code.overwrite( node.start, node.start + name.length, contextName, true );
111108
}
112109

113-
dependencies.push( ...contextDependencies.get( name ) );
114110
if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
115111
}
116112

@@ -135,7 +131,6 @@ export default class Generator {
135131
code.prependRight( node.start, `root.` );
136132
}
137133

138-
dependencies.push( name );
139134
if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' );
140135
}
141136

@@ -149,19 +144,52 @@ export default class Generator {
149144
}
150145
});
151146

147+
return {
148+
dependencies: expression._dependencies, // TODO probably a better way to do this
149+
contexts: usedContexts,
150+
snippet: `[✂${expression.start}-${expression.end}✂]`
151+
};
152+
}
153+
154+
findDependencies ( contextDependencies, expression ) {
155+
if ( expression._dependencies ) return expression._dependencies;
156+
157+
let scope = annotateWithScopes( expression );
158+
const dependencies = [];
159+
160+
walk( expression, {
161+
enter ( node, parent ) {
162+
if ( node._scope ) {
163+
scope = node._scope;
164+
return;
165+
}
166+
167+
if ( isReference( node, parent ) ) {
168+
const { name } = flattenReference( node );
169+
if ( scope.has( name ) ) return;
170+
171+
if ( contextDependencies.has( name ) ) {
172+
dependencies.push( ...contextDependencies.get( name ) );
173+
} else {
174+
dependencies.push( name );
175+
}
176+
177+
this.skip();
178+
}
179+
},
180+
181+
leave ( node ) {
182+
if ( node._scope ) scope = scope.parent;
183+
}
184+
});
185+
152186
dependencies.forEach( name => {
153187
if ( !globalWhitelist.has( name ) ) {
154188
this.expectedProperties.add( name );
155189
}
156190
});
157191

158-
expression._contextualised = {
159-
dependencies,
160-
contexts: usedContexts,
161-
snippet: `[✂${expression.start}-${expression.end}✂]`
162-
};
163-
164-
return expression._contextualised;
192+
return ( expression._dependencies = dependencies );
165193
}
166194

167195
generate ( result, options, { name, format } ) {

src/generators/dom/Block.js

+17-14
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 ) {
78+
return this.generator.findDependencies( this.contextDependencies, expression );
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() ) {
@@ -146,9 +154,4 @@ export default class Block {
146154
}
147155
`;
148156
}
149-
150-
tmp () {
151-
if ( !this._tmp ) this._tmp = this.getUniqueName( 'tmp' );
152-
return this._tmp;
153-
}
154157
}

src/generators/dom/index.js

+7-15
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ class DomGenerator extends Generator {
2424
};
2525
}
2626

27-
addBlock ( block ) {
28-
this.blocks.push( block );
29-
}
30-
3127
helper ( name ) {
3228
if ( this.options.dev && `${name}Dev` in shared ) {
3329
name = `${name}Dev`;
@@ -59,16 +55,14 @@ export default function dom ( parsed, source, options ) {
5955
visit( generator, block, state, node );
6056
});
6157

62-
generator.addBlock( block );
63-
6458
const builders = {
6559
main: new CodeBuilder(),
6660
init: new CodeBuilder(),
6761
_set: new CodeBuilder()
6862
};
6963

7064
if ( options.dev ) {
71-
builders._set.addBlock ( deindent`
65+
builders._set.addBlock( deindent`
7266
if ( typeof newState !== 'object' ) {
7367
throw new Error( 'Component .set was called without an object of data key-values to update.' );
7468
}
@@ -113,12 +107,9 @@ export default function dom ( parsed, source, options ) {
113107
builders._set.addLine( `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )` );
114108
}
115109

116-
// TODO is the `if` necessary?
117-
builders._set.addBlock( deindent`
118-
${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );
119-
if ( this._fragment ) this._fragment.update( newState, this._state );
120-
${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );
121-
` );
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 );` );
122113

123114
if ( hasJs ) {
124115
builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );
@@ -137,8 +128,9 @@ export default function dom ( parsed, source, options ) {
137128
` );
138129
}
139130

140-
let i = generator.blocks.length;
141-
while ( i-- ) builders.main.addBlock( generator.blocks[i].render() );
131+
generator.blocks.forEach( block => {
132+
builders.main.addBlock( block.render() );
133+
});
142134

143135
builders.init.addLine( `this._torndown = false;` );
144136

src/generators/dom/preprocess.js

+36-7
Original file line numberDiff line numberDiff line change
@@ -6,21 +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

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

2533
if ( isElseIf( node.else ) ) {
2634
attachBlocks( node.else.children[0] );
@@ -29,16 +37,27 @@ const preprocessors = {
2937
name: generator.getUniqueName( `create_if_block` )
3038
});
3139

40+
blocks.push( node.else._block );
3241
preprocessChildren( generator, node.else._block, node.else.children );
33-
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+
}
3447
}
3548
}
3649

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

4059
EachBlock: ( generator, block, node ) => {
41-
const { dependencies } = block.contextualise( node.expression );
60+
const dependencies = block.findDependencies( node.expression );
4261
block.addDependencies( dependencies );
4362

4463
const indexNames = new Map( block.indexNames );
@@ -77,15 +96,19 @@ const preprocessors = {
7796
params: block.params.concat( listName, context, indexName )
7897
});
7998

99+
generator.blocks.push( node._block );
80100
preprocessChildren( generator, node._block, node.children );
81101
block.addDependencies( node._block.dependencies );
102+
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
82103

83104
if ( node.else ) {
84105
node.else._block = block.child({
85106
name: generator.getUniqueName( `${node._block.name}_else` )
86107
});
87108

109+
generator.blocks.push( node.else._block );
88110
preprocessChildren( generator, node.else._block, node.else.children );
111+
node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0;
89112
}
90113
},
91114

@@ -94,14 +117,14 @@ const preprocessors = {
94117
if ( attribute.type === 'Attribute' && attribute.value !== true ) {
95118
attribute.value.forEach( chunk => {
96119
if ( chunk.type !== 'Text' ) {
97-
const { dependencies } = block.contextualise( chunk.expression );
120+
const dependencies = block.findDependencies( chunk.expression );
98121
block.addDependencies( dependencies );
99122
}
100123
});
101124
}
102125

103126
else if ( attribute.type === 'Binding' ) {
104-
const { dependencies } = block.contextualise( attribute.value );
127+
const dependencies = block.findDependencies( attribute.value );
105128
block.addDependencies( dependencies );
106129
}
107130
});
@@ -115,7 +138,10 @@ const preprocessors = {
115138
name: generator.getUniqueName( `create_${name}_yield_fragment` )
116139
});
117140

141+
generator.blocks.push( node._block );
118142
preprocessChildren( generator, node._block, node.children );
143+
block.addDependencies( node._block.dependencies );
144+
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
119145
}
120146

121147
else {
@@ -141,6 +167,7 @@ export default function preprocess ( generator, children ) {
141167

142168
contexts: new Map(),
143169
indexes: new Map(),
170+
contextDependencies: new Map(),
144171

145172
params: [ 'root' ],
146173
indexNames: new Map(),
@@ -149,7 +176,9 @@ export default function preprocess ( generator, children ) {
149176
dependencies: new Set()
150177
});
151178

179+
generator.blocks.push( block );
152180
preprocessChildren( generator, block, children );
181+
block.hasUpdateMethod = block.dependencies.size > 0;
153182

154183
return block;
155184
}

src/generators/dom/visitors/Component/Component.js

-2
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,6 @@ export default function visitComponent ( generator, block, state, node ) {
118118
);
119119

120120
componentInitProperties.push( `_yield: ${yieldFragment}`);
121-
122-
generator.addBlock( childBlock );
123121
}
124122

125123
const statements = [];

0 commit comments

Comments
 (0)