Skip to content

Instantly share code, notes, and snippets.

@thomaswilburn
Created December 3, 2023 03:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomaswilburn/dbb41b931df03f243f23d0f4a3b0f69e to your computer and use it in GitHub Desktop.
Save thomaswilburn/dbb41b931df03f243f23d0f4a3b0f69e to your computer and use it in GitHub Desktop.
Benchmark Lit iteration vs. replaceChildren
<!doctype html>
<ul id="ul"></ul>
<script type="module">
import { html, render } from "https://unpkg.com/lit-html";
import { repeat } from "https://unpkg.com/lit-html/directives/repeat.js";
import { map } from "https://unpkg.com/lit-html/directives/map.js";
function replacer(container, data, factory, update = () => {}) {
}
var guid = 0;
function mutate(list) {
var pick = () => Math.floor(Math.random() * list.length);
// add 100 items
for (var i = 0; i < 100; i++) {
list.splice(pick(), 0, { key: ++guid });
}
// remove 100 items
for (var i = 0; i < 100; i++) {
list.splice(pick(), 1);
}
// shuffle 100 items
for (var i = 0; i < 100; i++) {
var a = pick();
var b = pick();
var swap = list[a];
list[a] = list[b];
list[b] = swap;
}
}
class Replacer {
mapping = new WeakMap();
constructor(container, factory = () => document.createElement("li")) {
this.container = container;
this.factory = factory;
}
sync(data, update = () => {}) {
var elements = data.map(d => {
var e = this.mapping.get(d);
if (!e) {
e = this.factory(d);
this.mapping.set(d, e);
}
update(d, e);
return e;
});
this.container.replaceChildren(...elements);
}
}
var tick = () => new Promise(ok => requestAnimationFrame(ok));
async function testLit() {
ul.innerHTML = "";
var template = data => html`${repeat(data, d => d.key, d => html`<li> Item: ${d.key}`)}`;
// var template = data => html`${map(data, d => html`<li> Item: ${d.key}`)}`;
var data = [];
for (var i = 0; i < 1000; i++) {
data.push({ key: ++guid });
}
var times = [];
// initial render
render(template(data), ul);
for (var i = 0; i < 100; i++) {
mutate(data);
var start = performance.now();
render(template(data), ul);
var elapsed = performance.now() - start;
times.push(elapsed);
await tick();
}
var avg = times.reduce((t, i) => t + i, 0) / times.length;
console.log("Lit time:", avg);
var min = Math.min(...times);
var max = Math.max(...times);
console.log(`Minimum: ${min}, Maximum: ${max}`)
}
async function testReplace() {
ul.innerHTML = "";
var factory = d => {
var li = document.createElement("li");
li.innerHTML = `Item: ${d.key}`;
return li;
}
var binding = new Replacer(ul, factory);
var data = [];
for (var i = 0; i < 1000; i++) {
data.push({ key: ++guid });
}
var times = [];
// initial render
binding.sync(data);
for (var i = 0; i < 100; i++) {
mutate(data);
var start = performance.now();
binding.sync(data);
var elapsed = performance.now() - start;
times.push(elapsed);
await tick();
}
var avg = times.reduce((t, i) => t + i, 0) / times.length;
console.log("replaceChildren time:", avg);
var min = Math.min(...times);
var max = Math.max(...times);
console.log(`Minimum: ${min}, Maximum: ${max}`)
}
await testLit();
await testReplace();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment