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

@ -18,7 +18,7 @@ export default {
generator.addSourcemapLocations( node.expression );
generator.contextualise( node.expression );
const { dependencies } = generator.contextualise( node.expression );
const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`;
generator.current.updateStatements.push( deindent`
@ -30,7 +30,7 @@ export default {
}
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 ) {
@ -61,7 +61,10 @@ export default {
const indexes = Object.assign( {}, generator.current.indexes );
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 = {
useAnchor: false,
@ -70,12 +73,13 @@ export default {
expression: node.expression,
context: node.context,
contextDependencies,
contexts,
indexes,
indexNames,
listNames,
contextChain,
params,
initStatements: [],
updateStatements: [ Object.keys( contexts ).map( contextName => {

@ -40,10 +40,18 @@ export default {
}
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`
${name}.set({
${local.dynamicAttributes.join( ',\n' )}
});
var ${name}_changes = {};
${updates.join( '\n' )}
if ( Object.keys( ${name}_changes ).length ) ${name}.set( ${name}_changes );
` );
}

@ -60,11 +60,11 @@ export default {
${ifFalse.join( '\n\n' )}
}
if ( ${name} ) ${name}.update( ${generator.current.contextChain.join( ', ' )} );
if ( ${name} ) ${name}.update( ${generator.current.params.join( ', ' )} );
`;
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 );

@ -13,12 +13,12 @@ export default {
generator.addSourcemapLocations( node.expression );
const usedContexts = generator.contextualise( node.expression );
const { contexts } = generator.contextualise( node.expression );
const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`;
if ( isReference( node.expression ) ) {
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`
if ( ${snippet} !== ${name}_value ) {

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

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