0
0
mirror of https://github.com/sveltejs/svelte.git synced 2024-11-30 00:46:29 +01:00
* Performance Improvement with Keyed EachBlock
  * All DOM nodes for existing data are reused between changes to state
  * Speed up Keyed Swap Rows Benchmark
    * https://github.com/krausest/js-framework-benchmark
* Fixed Build
* Introduced jsdom.VirtualConsole
This commit is contained in:
Brian Takita 2018-03-17 00:40:15 -04:00
parent baff9bc6d1
commit 66e4df11a7
4 changed files with 72 additions and 101 deletions

View File

@ -1,7 +1,6 @@
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import Node from './shared/Node'; import Node from './shared/Node';
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import { DomGenerator } from '../dom/index';
import Block from '../dom/Block'; import Block from '../dom/Block';
import createDebuggingComment from '../../utils/createDebuggingComment'; import createDebuggingComment from '../../utils/createDebuggingComment';
@ -254,6 +253,9 @@ export default class EachBlock extends Node {
const head = block.getUniqueName(`${each}_head`); const head = block.getUniqueName(`${each}_head`);
const last = block.getUniqueName(`${each}_last`); const last = block.getUniqueName(`${each}_last`);
const expected = block.getUniqueName(`${each}_expected`); const expected = block.getUniqueName(`${each}_expected`);
const keep = block.getUniqueName(`${each}_keep`);
const mounts = block.getUniqueName(`${each}_mounts`);
const next_iteration = block.getUniqueName(`${each}_next_iteration`);
block.addVariable(lookup, `@blankObject()`); block.addVariable(lookup, `@blankObject()`);
block.addVariable(head); block.addVariable(head);
@ -317,12 +319,11 @@ export default class EachBlock extends Node {
`); `);
const dynamic = this.block.hasUpdateMethod; const dynamic = this.block.hasUpdateMethod;
let fn_destroy;
let destroy;
if (this.block.hasOutroMethod) { if (this.block.hasOutroMethod) {
const fn = block.getUniqueName(`${each}_outro`); fn_destroy = block.getUniqueName(`${each}_outro`);
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
function ${fn}(iteration) { function ${fn_destroy}(iteration) {
iteration.o(function() { iteration.o(function() {
iteration.u(); iteration.u();
iteration.d(); iteration.d();
@ -330,62 +331,43 @@ export default class EachBlock extends Node {
}); });
} }
`); `);
destroy = deindent`
while (${expected}) {
${fn}(${expected});
${expected} = ${expected}.next;
}
for (#i = 0; #i < discard_pile.length; #i += 1) {
if (discard_pile[#i].discard) {
${fn}(discard_pile[#i]);
}
}
`;
} else { } else {
const fn = block.getUniqueName(`${each}_destroy`); fn_destroy = block.getUniqueName(`${each}_destroy`);
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
function ${fn}(iteration) { function ${fn_destroy}(iteration) {
iteration.u(); var first = iteration.first
if (first && first.parentNode) {
iteration.u();
}
iteration.d(); iteration.d();
${lookup}[iteration.key] = null; ${lookup}[iteration.key] = null;
} }
`); `);
destroy = deindent`
while (${expected}) {
${fn}(${expected});
${expected} = ${expected}.next;
}
for (#i = 0; #i < discard_pile.length; #i += 1) {
var ${iteration} = discard_pile[#i];
if (${iteration}.discard) {
${fn}(${iteration});
}
}
`;
} }
const destroy = deindent`
${iteration} = ${head};
while(${iteration}) {
if (!${keep}[${iteration}.key]) {
${fn_destroy}(${iteration});
}
${iteration} = ${iteration}.next;
}
`;
block.builders.update.addBlock(deindent` block.builders.update.addBlock(deindent`
var ${each_block_value} = ${snippet}; var ${each_block_value} = ${snippet};
var ${expected} = ${head}; var ${expected} = ${head};
var ${last} = null; var ${last} = null;
var rendered = {}; var ${keep} = {};
var all = {}; var ${mounts} = {};
var ${next_iteration} = null;
var each_all = ${head};
while(each_all) {
all[each_all.key] = each_all;
each_all = each_all.next;
}
for (#i = 0; #i < ${each_block_value}.${length}; #i += 1) { for (#i = 0; #i < ${each_block_value}.${length}; #i += 1) {
var ${key} = ${each_block_value}[#i].${this.key}; var ${key} = ${each_block_value}[#i].${this.key};
var ${iteration} = ${lookup}[${key}]; var ${iteration} = ${lookup}[${key}];
var next_data = ${each_block_value}[#i+1];
var next = next_data && ${lookup}[next_data.${this.key}];
var ${this.each_context} = @assign({}, state, { var ${this.each_context} = @assign({}, state, {
${this.contextProps.join(',\n')} ${this.contextProps.join(',\n')}
@ -393,66 +375,52 @@ export default class EachBlock extends Node {
${dynamic && ${dynamic &&
`if (${iteration}) ${iteration}.p(changed, ${this.each_context});`} `if (${iteration}) ${iteration}.p(changed, ${this.each_context});`}
if (${expected} && (${key} === ${expected}.key)) {
if (${expected}) { var first = ${iteration} && ${iteration}.first;
if (${key} === ${expected}.key) { var parentNode = first && first.parentNode
${expected} = ${expected}.next; if (!parentNode || (${iteration} && ${iteration}.next) != next) ${mounts}[${key}] = ${iteration};
} else { ${expected} = ${iteration}.next;
if (${iteration}) { } else if (${iteration}) {
${mounts}[${key}] = ${iteration};
var next_data = ${each_block_value}[#i+1]; ${expected} = ${iteration}.next;
var next = next_data && ${lookup}[next_data.id];
var first = ${iteration}.first;
var first_next = next && next.first;
///insertNode(first, tbody, first_next);
${updateMountNode}.insertBefore(first, first_next);
${expected} = next;
${iteration}.next = ${expected};
var prev_data = ${each_block_value}[#i-1];
var prev = prev_data && ${lookup}[prev_data.id];
if (prev) {
prev.next = ${iteration};
}
} else {
// key is being inserted
${iteration} = ${lookup}[${key}] = ${create_each_block}(#component, ${key}, ${this.each_context});
${iteration}.c();
${iteration}.${mountOrIntro}(${updateMountNode}, ${expected}.first);
${expected}.last = ${iteration};
${iteration}.next = ${expected};
}
}
} else { } else {
// we're appending from this point forward // key is being inserted
if (${iteration}) { ${iteration} = ${lookup}[${key}] = ${create_each_block}(#component, ${key}, ${this.each_context});
${iteration}.next = null; ${iteration}.c();
${iteration}.m(${updateMountNode}, ${anchor}); ${mounts}[${key}] = ${iteration};
} else {
${iteration} = ${lookup}[${key}] = ${create_each_block}(#component, ${key}, ${this.each_context});
${iteration}.c();
${iteration}.${mountOrIntro}(${updateMountNode}, ${anchor});
}
} }
if (${iteration}) { ${lookup}[${key}] = ${iteration};
rendered[${iteration}.key] = ${iteration}; ${keep}[${iteration}.key] = ${iteration};
}
if (${last}) ${last}.next = ${iteration};
${iteration}.last = ${last};
${this.block.hasIntroMethod && `${iteration}.i(${updateMountNode}, ${anchor});`}
${last} = ${iteration}; ${last} = ${iteration};
} }
${destroy}
if (${last}) ${last}.next = null; // Work backwards due to DOM api having insertBefore
for (#i = ${each_block_value}.${length} - 1; #i >= 0; #i -= 1) {
for (var key_all in all) { var data = ${each_block_value}[#i];
if (!rendered[key_all]) all[key_all].d(); var ${key} = data.${this.key};
} ${iteration} = ${lookup}[${key}];
if (${mounts}[${key}]) {
var anchor;
${this.block.hasOutroMethod
? deindent`
var key_next_iteration = ${next_iteration} && ${next_iteration}.key;
var iteration_anchor = ${iteration}.next;
var key_anchor;
do {
anchor = iteration_anchor && iteration_anchor.first;
iteration_anchor = iteration_anchor && iteration_anchor.next;
key_anchor = iteration_anchor && iteration_anchor.key;
} while(iteration_anchor && key_anchor != key_next_iteration && !${keep}[key_anchor])`
: deindent`
anchor = ${next_iteration} && ${next_iteration}.first;
` }
${mounts}[${key}].${mountOrIntro}(${updateMountNode}, anchor);
}
${iteration}.next = ${next_iteration};
if (${next_iteration}) ${next_iteration}.last = ${iteration};
${next_iteration} = ${iteration};
}
${head} = ${lookup}[${each_block_value}[0] && ${each_block_value}[0].${this.key}]; ${head} = ${lookup}[${each_block_value}[0] && ${each_block_value}[0].${this.key}];
`); `);

View File

@ -1,4 +1,3 @@
import { assign, noop } from './utils.js';
import { createElement } from './dom.js'; import { createElement } from './dom.js';
export function linear(t) { export function linear(t) {

View File

@ -1,3 +1,4 @@
import jsdom from 'jsdom';
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import assert from 'assert'; import assert from 'assert';
import glob from 'glob'; import glob from 'glob';
@ -45,7 +46,8 @@ export function tryToReadFile(file) {
} }
} }
const { window } = new JSDOM('<main></main>'); export const virtualConsole = new jsdom.VirtualConsole();
const { window } = new JSDOM('<main></main>', {virtualConsole});
global.document = window.document; global.document = window.document;
export function env() { export function env() {

View File

@ -41,6 +41,8 @@ export default {
test( 'g' ); test( 'g' );
test( '' ); test( '' );
test( 'abc' ); test( 'abc' );
test( 'duqbmineapjhtlofrskcg' );
test( 'hdnkjougmrvftewsqpailcb' );
// then, we party // then, we party
for ( let i = 0; i < 100; i += 1 ) test( permute() ); for ( let i = 0; i < 100; i += 1 ) test( permute() );