重新排序DOM节点时的HyperHTML性能

时间:2018-09-06 11:26:15

标签: javascript dom template-literals virtual-dom hyperhtml

我正在尝试了解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表现得更好?

1 个答案:

答案 0 :(得分:1)

更新

hyperHTML 2.14引入了domdiff V2的用法,该用法带来了像petit-dom一样的性能,但为此库增加了0.6K的成本,希望该更改值得。

由于一些奇怪的原因,原始演示在Safari上也遇到了很大的问题,最有可能与事实节点有关的事实直接作为TR附加到TABLE上,而不是附加到了表TBODY元素上。

无论如何,您现在可以比较有线演示through this CodePen的性能。

请注意,关于旧鼻息肉扩散的所有说法也不再适用。


自从您获得bind以来,您似乎可以读得更远一些,达到the wire part

基本上,如果您使用字符串数组作为内插值,则您所做的工作与类似innerHTML的操作一样,并具有所有常规的副作用:

  • 每次删除所有节点并从头开始创建它们时
  • 对任何节点的所有引用将永远丢失
  • GC操作量更高
  • 布局容易出现XSS,因此不安全

为避免复制所有这些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,其目的是重新组织节点。

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?对于日常的现实世界任务没那么有趣。

我希望这能回答您的所有疑问。