only update components if their dependencies have changed

pull/31/head
Rich-Harris 8 years ago
parent 978a2bcae7
commit c523fc74aa

@ -21,7 +21,7 @@ export default function generate ( parsed, source, options ) {
${fragment.initStatements.join( '\n\n' )} ${fragment.initStatements.join( '\n\n' )}
return { return {
update: function ( ${fragment.contextChain.join( ', ' )} ) { update: function ( ${fragment.params.join( ', ' )} ) {
${fragment.updateStatements.join( '\n\n' )} ${fragment.updateStatements.join( '\n\n' )}
}, },
@ -48,9 +48,9 @@ export default function generate ( parsed, source, options ) {
contextualise ( expression, isEventHandler ) { contextualise ( expression, isEventHandler ) {
const usedContexts = []; const usedContexts = [];
const dependencies = [];
const contexts = generator.current.contexts; const { contextDependencies, contexts, indexes } = generator.current;
const indexes = generator.current.indexes;
walk( expression, { walk( expression, {
enter ( node, parent ) { enter ( node, parent ) {
@ -67,11 +67,13 @@ export default function generate ( parsed, source, options ) {
} }
if ( contexts[ name ] ) { if ( contexts[ name ] ) {
dependencies.push( ...contextDependencies[ name ] );
if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name ); if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
} else if ( indexes[ name ] ) { } else if ( indexes[ name ] ) {
const context = indexes[ name ]; const context = indexes[ name ];
if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context ); if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );
} else { } else {
dependencies.push( node.name );
generator.code.insertRight( node.start, `root.` ); generator.code.insertRight( node.start, `root.` );
if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' ); if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' );
} }
@ -81,7 +83,11 @@ export default function generate ( parsed, source, options ) {
} }
}); });
return usedContexts; return {
dependencies,
contexts: usedContexts,
snippet: `[✂${expression.start}-${expression.end}✂]`
};
}, },
// TODO use getName instead of counters // TODO use getName instead of counters
@ -205,7 +211,7 @@ export default function generate ( parsed, source, options ) {
contexts: {}, contexts: {},
indexes: {}, indexes: {},
contextChain: [ 'root' ], params: [ 'changed', 'root' ],
indexNames: {}, indexNames: {},
listNames: {}, listNames: {},
@ -258,7 +264,7 @@ export default function generate ( parsed, source, options ) {
setting = true; setting = true;
dispatchObservers( observers.immediate, newState, oldState ); dispatchObservers( observers.immediate, newState, oldState );
if ( mainFragment ) mainFragment.update( state ); if ( mainFragment ) mainFragment.update( newState, state );
dispatchObservers( observers.deferred, newState, oldState ); dispatchObservers( observers.deferred, newState, oldState );
setting = false; setting = false;
` ); ` );

@ -18,7 +18,7 @@ export default {
generator.addSourcemapLocations( node.expression ); generator.addSourcemapLocations( node.expression );
generator.contextualise( node.expression ); const { dependencies } = generator.contextualise( node.expression );
const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`; const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`;
generator.current.updateStatements.push( deindent` generator.current.updateStatements.push( deindent`
@ -30,7 +30,7 @@ export default {
} }
const iteration = ${name}_iterations[i]; const iteration = ${name}_iterations[i];
${name}_iterations[i].update( ${generator.current.contextChain.join( ', ' )}, ${listName}, ${listName}[i], i ); ${name}_iterations[i].update( ${generator.current.params.join( ', ' )}, ${listName}, ${listName}[i], i );
} }
for ( var i = ${name}_value.length; i < ${name}_iterations.length; i += 1 ) { for ( var i = ${name}_value.length; i < ${name}_iterations.length; i += 1 ) {
@ -61,7 +61,10 @@ export default {
const indexes = Object.assign( {}, generator.current.indexes ); const indexes = Object.assign( {}, generator.current.indexes );
if ( node.index ) indexes[ indexName ] = node.context; if ( node.index ) indexes[ indexName ] = node.context;
const contextChain = generator.current.contextChain.concat( listName, node.context, indexName ); const contextDependencies = Object.assign( {}, generator.current.contextDependencies );
contextDependencies[ node.context ] = dependencies;
const params = generator.current.params.concat( listName, node.context, indexName );
generator.current = { generator.current = {
useAnchor: false, useAnchor: false,
@ -70,12 +73,13 @@ export default {
expression: node.expression, expression: node.expression,
context: node.context, context: node.context,
contextDependencies,
contexts, contexts,
indexes, indexes,
indexNames, indexNames,
listNames, listNames,
contextChain, params,
initStatements: [], initStatements: [],
updateStatements: [ Object.keys( contexts ).map( contextName => { updateStatements: [ Object.keys( contexts ).map( contextName => {

@ -40,10 +40,18 @@ export default {
} }
if ( local.dynamicAttributes.length ) { if ( local.dynamicAttributes.length ) {
const updates = local.dynamicAttributes.map( attribute => {
return deindent`
if ( ${attribute.dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) ${name}_changes.${attribute.name} = ${attribute.value};
`;
});
local.update.push( deindent` local.update.push( deindent`
${name}.set({ var ${name}_changes = {};
${local.dynamicAttributes.join( ',\n' )}
}); ${updates.join( '\n' )}
if ( Object.keys( ${name}_changes ).length ) ${name}.set( ${name}_changes );
` ); ` );
} }

@ -60,11 +60,11 @@ export default {
${ifFalse.join( '\n\n' )} ${ifFalse.join( '\n\n' )}
} }
if ( ${name} ) ${name}.update( ${generator.current.contextChain.join( ', ' )} ); if ( ${name} ) ${name}.update( ${generator.current.params.join( ', ' )} );
`; `;
if ( node.else ) { if ( node.else ) {
update += `\nif ( ${elseName} ) ${elseName}.update( ${generator.current.contextChain.join( ', ' )} );`; update += `\nif ( ${elseName} ) ${elseName}.update( ${generator.current.params.join( ', ' )} );`;
} }
generator.current.updateStatements.push( update ); generator.current.updateStatements.push( update );

@ -13,12 +13,12 @@ export default {
generator.addSourcemapLocations( node.expression ); generator.addSourcemapLocations( node.expression );
const usedContexts = generator.contextualise( node.expression ); const { contexts } = generator.contextualise( node.expression );
const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`; const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`;
if ( isReference( node.expression ) ) { if ( isReference( node.expression ) ) {
const reference = `${generator.source.slice( node.expression.start, node.expression.end )}`; const reference = `${generator.source.slice( node.expression.start, node.expression.end )}`;
const qualified = usedContexts[0] === 'root' ? `root.${reference}` : reference; const qualified = contexts[0] === 'root' ? `root.${reference}` : reference;
generator.current.updateStatements.push( deindent` generator.current.updateStatements.push( deindent`
if ( ${snippet} !== ${name}_value ) { if ( ${snippet} !== ${name}_value ) {

@ -23,16 +23,21 @@ export default function addComponentAttributes ( generator, node, local ) {
else { else {
// simple dynamic attributes // simple dynamic attributes
generator.contextualise( value.expression ); const { dependencies, snippet } = generator.contextualise( value.expression );
const result = `[✂${value.expression.start}-${value.expression.end}✂]`;
// TODO only update attributes that have changed // TODO only update attributes that have changed
local.dynamicAttributes.push( `${attribute.name}: ${result}` ); local.dynamicAttributes.push({
name: attribute.name,
value: snippet,
dependencies
});
} }
} }
else { else {
// complex dynamic attributes // complex dynamic attributes
const allDependencies = [];
const value = ( attribute.value[0].type === 'Text' ? '' : `"" + ` ) + ( const value = ( attribute.value[0].type === 'Text' ? '' : `"" + ` ) + (
attribute.value.map( chunk => { attribute.value.map( chunk => {
if ( chunk.type === 'Text' ) { if ( chunk.type === 'Text' ) {
@ -40,14 +45,21 @@ export default function addComponentAttributes ( generator, node, local ) {
} else { } else {
generator.addSourcemapLocations( chunk.expression ); generator.addSourcemapLocations( chunk.expression );
generator.contextualise( chunk.expression ); const { dependencies } = generator.contextualise( chunk.expression );
dependencies.forEach( dependency => {
if ( !~allDependencies.indexOf( dependency ) ) allDependencies.push( dependency );
});
return `( [✂${chunk.expression.start}-${chunk.expression.end}✂] )`; return `( [✂${chunk.expression.start}-${chunk.expression.end}✂] )`;
} }
}).join( ' + ' ) }).join( ' + ' )
); );
// TODO only update attributes that have changed local.dynamicAttributes.push({
local.dynamicAttributes.push( `${attribute.name}: ${value}` ); name: attribute.name,
value,
dependencies: allDependencies
});
} }
} }
@ -58,7 +70,7 @@ export default function addComponentAttributes ( generator, node, local ) {
const usedContexts = new Set(); const usedContexts = new Set();
attribute.expression.arguments.forEach( arg => { attribute.expression.arguments.forEach( arg => {
const contexts = generator.contextualise( arg, true, true ); const { contexts } = generator.contextualise( arg, true, true );
contexts.forEach( context => { contexts.forEach( context => {
usedContexts.add( context ); usedContexts.add( context );

@ -115,7 +115,7 @@ export default function addElementAttributes ( generator, node, local ) {
const usedContexts = new Set(); const usedContexts = new Set();
attribute.expression.arguments.forEach( arg => { attribute.expression.arguments.forEach( arg => {
const contexts = generator.contextualise( arg, true ); const { contexts } = generator.contextualise( arg, true );
contexts.forEach( context => { contexts.forEach( context => {
usedContexts.add( context ); usedContexts.add( context );

@ -0,0 +1,15 @@
export default {
test ( assert, component ) {
const foo = component.refs.foo;
let count = 0;
foo.observe( 'x', () => {
count += 1;
});
assert.equal( count, 1 );
component.set({ y: {} });
assert.equal( count, 1 );
}
};

@ -0,0 +1,14 @@
<Foo ref:foo x='{{x}}'/>
<script>
import Foo from './Foo.html';
export default {
data: () => ({
x: {},
y: {}
}),
components: { Foo }
};
</script>
Loading…
Cancel
Save