Skip to content

Commit

Permalink
Merge pull request #490 from sveltejs/gh-472
Browse files Browse the repository at this point in the history
Only update dynamic subtrees
  • Loading branch information
Rich-Harris committed Apr 17, 2017
2 parents aeedb94 + 080afc9 commit fc31d39
Show file tree
Hide file tree
Showing 17 changed files with 336 additions and 155 deletions.
56 changes: 42 additions & 14 deletions src/generators/Generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,14 @@ export default class Generator {
}

contextualise ( block, expression, context, isEventHandler ) {
if ( expression._contextualised ) return expression._contextualised;

this.addSourcemapLocations( expression );

const usedContexts = [];
const dependencies = [];

const { code, helpers } = this;
const { contextDependencies, contexts, indexes } = block;
const { contexts, indexes } = block;

let scope = annotateWithScopes( expression );
let scope = annotateWithScopes( expression ); // TODO this already happens in findDependencies
let lexicalDepth = 0;

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

dependencies.push( ...contextDependencies.get( name ) );
if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
}

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

dependencies.push( name );
if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' );
}

Expand All @@ -149,19 +144,52 @@ export default class Generator {
}
});

return {
dependencies: expression._dependencies, // TODO probably a better way to do this
contexts: usedContexts,
snippet: `[✂${expression.start}-${expression.end}✂]`
};
}

findDependencies ( contextDependencies, expression ) {
if ( expression._dependencies ) return expression._dependencies;

let scope = annotateWithScopes( expression );
const dependencies = [];

walk( expression, {
enter ( node, parent ) {
if ( node._scope ) {
scope = node._scope;
return;
}

if ( isReference( node, parent ) ) {
const { name } = flattenReference( node );
if ( scope.has( name ) ) return;

if ( contextDependencies.has( name ) ) {
dependencies.push( ...contextDependencies.get( name ) );
} else {
dependencies.push( name );
}

this.skip();
}
},

leave ( node ) {
if ( node._scope ) scope = scope.parent;
}
});

dependencies.forEach( name => {
if ( !globalWhitelist.has( name ) ) {
this.expectedProperties.add( name );
}
});

expression._contextualised = {
dependencies,
contexts: usedContexts,
snippet: `[✂${expression.start}-${expression.end}✂]`
};

return expression._contextualised;
return ( expression._dependencies = dependencies );
}

generate ( result, options, { name, format } ) {
Expand Down
31 changes: 17 additions & 14 deletions src/generators/dom/Block.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export default class Block {
// unique names
this.component = this.getUniqueName( 'component' );
this.target = this.getUniqueName( 'target' );

this.hasUpdateMethod = false; // determined later
}

addDependencies ( dependencies ) {
Expand Down Expand Up @@ -72,6 +74,10 @@ export default class Block {
this.addElement( name, renderStatement, parentNode, true );
}

findDependencies ( expression ) {
return this.generator.findDependencies( this.contextDependencies, expression );
}

mount ( name, parentNode ) {
if ( parentNode ) {
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${name}, ${parentNode} );` );
Expand Down Expand Up @@ -115,15 +121,17 @@ export default class Block {
` );
}

if ( this.builders.update.isEmpty() ) {
properties.addBlock( `update: ${this.generator.helper( 'noop' )},` );
} else {
if ( this._tmp ) this.builders.update.addBlockAtStart( `var ${this._tmp};` );
properties.addBlock( deindent`
update: function ( changed, ${this.params.join( ', ' )} ) {
${this.builders.update}
},
` );
if ( this.hasUpdateMethod ) {
if ( this.builders.update.isEmpty() ) {
properties.addBlock( `update: ${this.generator.helper( 'noop' )},` );
} else {
if ( this._tmp ) this.builders.update.addBlockAtStart( `var ${this._tmp};` );
properties.addBlock( deindent`
update: function ( changed, ${this.params.join( ', ' )} ) {
${this.builders.update}
},
` );
}
}

if ( this.builders.destroy.isEmpty() ) {
Expand All @@ -146,9 +154,4 @@ export default class Block {
}
`;
}

tmp () {
if ( !this._tmp ) this._tmp = this.getUniqueName( 'tmp' );
return this._tmp;
}
}
22 changes: 7 additions & 15 deletions src/generators/dom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ class DomGenerator extends Generator {
};
}

addBlock ( block ) {
this.blocks.push( block );
}

helper ( name ) {
if ( this.options.dev && `${name}Dev` in shared ) {
name = `${name}Dev`;
Expand Down Expand Up @@ -59,16 +55,14 @@ export default function dom ( parsed, source, options ) {
visit( generator, block, state, node );
});

generator.addBlock( block );

const builders = {
main: new CodeBuilder(),
init: new CodeBuilder(),
_set: new CodeBuilder()
};

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

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

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

let i = generator.blocks.length;
while ( i-- ) builders.main.addBlock( generator.blocks[i].render() );
generator.blocks.forEach( block => {
builders.main.addBlock( block.render() );
});

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

Expand Down
43 changes: 36 additions & 7 deletions src/generators/dom/preprocess.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,29 @@ function isElseIf ( node ) {

const preprocessors = {
MustacheTag: ( generator, block, node ) => {
const { dependencies } = block.contextualise( node.expression );
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );
},

IfBlock: ( generator, block, node ) => {
const blocks = [];
let dynamic = false;

function attachBlocks ( node ) {
const { dependencies } = block.contextualise( node.expression );
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );

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

blocks.push( node._block );
preprocessChildren( generator, node._block, node.children );
block.addDependencies( node._block.dependencies );

if ( node._block.dependencies.size > 0 ) {
dynamic = true;
block.addDependencies( node._block.dependencies );
}

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

blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else.children );
block.addDependencies( node.else._block.dependencies );

if ( node.else._block.dependencies.size > 0 ) {
dynamic = true;
block.addDependencies( node.else._block.dependencies );
}
}
}

attachBlocks( node );

blocks.forEach( block => {
block.hasUpdateMethod = dynamic;
});

generator.blocks.push( ...blocks );
},

EachBlock: ( generator, block, node ) => {
const { dependencies } = block.contextualise( node.expression );
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );

const indexNames = new Map( block.indexNames );
Expand Down Expand Up @@ -77,15 +96,19 @@ const preprocessors = {
params: block.params.concat( listName, context, indexName )
});

generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node.children );
block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;

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

generator.blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else.children );
node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0;
}
},

Expand All @@ -94,14 +117,14 @@ const preprocessors = {
if ( attribute.type === 'Attribute' && attribute.value !== true ) {
attribute.value.forEach( chunk => {
if ( chunk.type !== 'Text' ) {
const { dependencies } = block.contextualise( chunk.expression );
const dependencies = block.findDependencies( chunk.expression );
block.addDependencies( dependencies );
}
});
}

else if ( attribute.type === 'Binding' ) {
const { dependencies } = block.contextualise( attribute.value );
const dependencies = block.findDependencies( attribute.value );
block.addDependencies( dependencies );
}
});
Expand All @@ -115,7 +138,10 @@ const preprocessors = {
name: generator.getUniqueName( `create_${name}_yield_fragment` )
});

generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node.children );
block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
}

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

contexts: new Map(),
indexes: new Map(),
contextDependencies: new Map(),

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

generator.blocks.push( block );
preprocessChildren( generator, block, children );
block.hasUpdateMethod = block.dependencies.size > 0;

return block;
}
2 changes: 0 additions & 2 deletions src/generators/dom/visitors/Component/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,6 @@ export default function visitComponent ( generator, block, state, node ) {
);

componentInitProperties.push( `_yield: ${yieldFragment}`);

generator.addBlock( childBlock );
}

const statements = [];
Expand Down
Loading

0 comments on commit fc31d39

Please sign in to comment.