我正在尝试了解HyperHTML以及如何从中获得最佳性能。
在阅读how it works under the hood时,似乎暗示着模板与DOM之间建立了牢固的联系,这意味着它需要与VirtualDOM不同的思维方式来优化性能。
我编写了一些代码,以显示使用hyperHtml和normalHtml对表中的 N 元素进行排序。 normalHtml版本仅刷新表并重建元素。它们在性能方面似乎都相似。我在这里比较苹果和桔子吗?如何使hyperHtml版本的代码性能更好?
代码:
const numberOfElements = 10000
const items = Array.apply(null, Array(numberOfElements)).map((el, i) => i).sort(() => .5 - Math.random())
const sortMethods = [
() => 0,
(a, b) => a - b,
(a, b) => b - a
]
function hyperHtmlTest() {
const $node = document.createElement('div')
const $table = document.createElement('table')
const $button = document.createElement('button')
const tableRender = hyperHTML.bind($table)
let sortMethodIndex = 0
function render () {
const sortMethod = sortMethods[sortMethodIndex++ % sortMethods.length]
tableRender`${
items.concat().sort(sortMethod).map(item => {
return `<tr><td>${item}</td></tr>`
})
}`
}
$node.appendChild($button)
$node.appendChild($table)
$button.textContent = 'HyperHTml Sort'
$button.onclick = render
return $node
}
function normalHtmlTest() {
const $node = document.createElement('div')
const $table = document.createElement('table')
const $button = document.createElement('button')
let sortMethodIndex = 0
function render () {
const sortMethod = sortMethods[sortMethodIndex++ % sortMethods.length]
while($table.childNodes.length)
$table.removeChild($table.childNodes[0])
const frag = document.createDocumentFragment()
items.concat().sort(sortMethod).forEach(item => {
const tr = document.createElement('tr')
const td = document.createElement('td')
td.textContent = item
tr.appendChild(td)
frag.appendChild(tr)
})
$table.appendChild(frag)
}
$node.appendChild($button)
$node.appendChild($table)
$button.textContent = 'NormalHtml Sort'
$button.onclick = render
return $node
}
document.body.appendChild(hyperHtmlTest())
document.body.appendChild(normalHtmlTest())
或在CodePen
上总结问题:为什么在我的代码示例中HyperHTML像普通的DOM操作一样表现出色,以及如何在重新排序DOM节点时使HyperHTML表现得更好?
答案 0 :(得分:1)
更新
hyperHTML 2.14引入了domdiff V2的用法,该用法带来了像petit-dom一样的性能,但为此库增加了0.6K的成本,希望该更改值得。
由于一些奇怪的原因,原始演示在Safari上也遇到了很大的问题,最有可能与事实节点有关的事实直接作为TR附加到TABLE上,而不是附加到了表TBODY元素上。
无论如何,您现在可以比较有线演示through this CodePen的性能。
请注意,关于旧鼻息肉扩散的所有说法也不再适用。
自从您获得bind
以来,您似乎可以读得更远一些,达到the wire part。
基本上,如果您使用字符串数组作为内插值,则您所做的工作与类似innerHTML
的操作一样,并具有所有常规的副作用:
为避免复制所有这些innerHTML
陷阱并正确使用hyperHTML
,您需要将wire
个项目放到它们表示的节点上。
tableRender`${
items.concat().sort(sortMethod).map(item => {
return wire(item)`<tr><td>${item.text}</td></tr>`
})
}`
但是,由于考虑到内存问题,电汇通过WeakMap
工作,因此除非用作电汇的:ids ,否则数字不是很好。
现实情况是,在现实世界中没有人将数字或字符串作为项目,对象中有99%的时间是数字,因此,出于演示目的,让对象为对象。
Array.apply(null, Array(numberOfElements)).map(
(el, i) => new Number(i)
)
一旦有了对象而不是基元,这是我为演示而创建的对象的旁边,这是一个更现实的场景,每次调用渲染或更新时,行都不会被丢弃并在每次重新创建时,这些都将简单地重新排序,就像您在my CodePen fork中所看到的那样,基本上总结为:
function hyperHtmlTest() {
const {bind, wire} = hyperHTML;
const render = bind(document.createElement('div'));
let sortMethodIndex = 0;
return update();
function update() {
const sortMethod = sortMethods[sortMethodIndex++ % sortMethods.length];
return render`
<button onclick=${update}>HyperHTml Sort</button>
<table>
${items.concat().sort(sortMethod).map(
item => wire(item)`<tr><td>${+item}</td></tr>`
)}
</table>`;
}
}
关于效果
hyperHTML 后面是an engine called domdiff,其目的是重新组织节点。
您可以轻松地在笔上看到,当您从ASC切换到DESC或反之亦然时,它的速度比其原始DOM同类产品快5倍,但是当涉及到有序列表以完全随机的形式时, domdiff 进行了大量检查,以确保DOM完全不关心,因此速度可能会变慢。
简而言之,虽然普通的DOM方法线性快(或慢),但算法有最好的情况和最坏的情况。
一种在几乎所有情况下都性能良好的算法是petit-dom使用的算法,但是整个逻辑部分的权重对于实际场景来说是IMO不必要的,但对于非现实基准来说肯定是令人印象深刻的。
70000行无汗
这些天来,我正在研究 hyperHTML 自定义元素,这并不是什么秘密,该元素旨在通过可排序和可搜索的表处理成千上万的行。
您可以see an early prototype in this page,并意识到框架如何处理10000个表行无关紧要,因为一个好的组件永远都不应放在DOM上,因此,许多节点不会导致任何用户看不到全部一次。
在该70K表中可以看到,排序是一闪而过,搜索和滚动也是如此,这就是 HyperHTMLElement 的全部荣耀。
These benchmarks?对于日常的现实世界任务没那么有趣。
我希望这能回答您的所有疑问。