Skip to content

Commit

Permalink
feat: performance improvements for keyed lists changes
Browse files Browse the repository at this point in the history
  • Loading branch information
lifeart committed May 31, 2019
1 parent 0fb6a36 commit fe950b9
Show file tree
Hide file tree
Showing 3 changed files with 548 additions and 4 deletions.
267 changes: 267 additions & 0 deletions packages/@glimmer/integration-tests/test/updating-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2708,6 +2708,273 @@ module('[jit] integration - Updating', hooks => {
}
});

test('non-keyed rendering in each loops produce expected mutations count', assert => {
let done = assert.async();
let items = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }];
let object = { data: items };
let template = compile(
"<ul class='non-keyed'>{{#each this.data key='@index' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('non-keyed');
let a = object.data[1];
let b = object.data[3];
object.data[3] = a;
object.data[1] = b;
let observer = null;
observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 2, 'we need only 2 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('Keyed rendering in each loops produce expected mutations count', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
let a = object.data[1];
let b = object.data[6];
object.data[6] = a;
object.data[1] = b;
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 6, 'we need only 6 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);

rerender();
});

test('Keyed rendering in each loops produce expected mutations count for corner cases', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
let a = object.data[0];
let b = object.data[7];
object.data[7] = a;
object.data[0] = b;
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 6, 'we need only 6 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('Keyed rendering in each loops produce expected mutations count for left semi-corner cases', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
let a = object.data[0];
let b = object.data[5];
object.data[5] = a;
object.data[0] = b;
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 6, 'we need only 6 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('Keyed rendering in each loops produce expected mutations count for right semi-corner cases', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
let a = object.data[7];
let b = object.data[5];
object.data[5] = a;
object.data[7] = b;
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 6, 'we need only 6 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('Keyed rendering in each loops produce expected mutations count for new item push appending case', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
items.push({ id: 9 });
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 3, 'we need only 3 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('Keyed rendering in each loops produce expected mutations count for new item unshift appending case', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
items.unshift({ id: 9 });
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 3, 'we need only 3 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('Keyed rendering in each loops produce expected mutations count for new item in the middle', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
items.splice(5, 0, { id: 9 });
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 3, 'we need only 3 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('Keyed rendering in each loops produce expected mutations count for 4 items swap', assert => {
let done = assert.async();
let items = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
];
let object = { data: items };
let template = compile(
"<ul class='keyed'>{{#each this.data key='id' as |row|}}<li>{{row.id}}</li>{{/each}}</ul>"
);
render(template, object);
const listNode: any = getNodeByClassName('keyed');
let a = items[1];
let b = items[3];
let c = items[4];
let d = items[6];
items[6] = b;
items[4] = a;
items[3] = d;
items[1] = c;
let observer = new MutationObserver(mutations => {
assert.equal(mutations.length, 10, 'we need only 10 dom mutations for this case');
done();
});
let config = { attributes: true, childList: true, characterData: true };
observer.observe(listNode, config);
rerender();
});

test('The each helper yields the index of the current item when using a non-@index key', assert => {
let tom = { key: '1', name: 'Tom Dale', class: 'tomdale' };
let yehuda = { key: '2', name: 'Yehuda Katz', class: 'wycats' };
Expand Down
9 changes: 5 additions & 4 deletions packages/@glimmer/reference/lib/iterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ export class IterationArtifacts {

move(item: ListItem, reference: Option<ListItem>): void {
let { list } = this;

item.retained = true;
list.remove(item);
list.insertBefore(item, reference);
Expand Down Expand Up @@ -264,8 +263,7 @@ export class IteratorSynchronizer<Env> {
}

private advanceToKey(key: unknown) {
let { current, artifacts } = this;

let { current, artifacts, target } = this;
let seek = current;

while (seek !== null && seek.key !== key) {
Expand All @@ -274,7 +272,10 @@ export class IteratorSynchronizer<Env> {
}

if (seek !== null) {
this.current = artifacts.nextNode(seek);
artifacts.move(seek, current);
target.move(this.env, seek.key, seek.value, seek.memo, current ? current.key : END);

this.current = artifacts.nextNode(current as ListItem);
}
}

Expand Down
Loading

0 comments on commit fe950b9

Please sign in to comment.