2

我正在尝试了解HyperHTML以及如何从中获得最佳性能。

阅读它的底层工作原理,似乎暗示模板和 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 更高效?

4

1 回答 1

2

更新

hyperHTML 2.14 引入了 domdiff V2 的使用,它带来了类似 petit-dom 的性能,但需要为库增加 0.6K 的成本,希望值得改变。

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

无论如何,您现在可以通过这个 CodePen比较有线演示的性能。

请注意,关于旧的 snabdom diffing 的所有内容也不再相关。


看起来你可以读得更远一点,到达电线部分,因为你得到了那个bind

基本上,如果您使用字符串数组作为内插值,那么您所做的与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)
)

一旦你有了对象而不是基元,这是我为演示而创建的那种对象,一个更现实的场景,每次你调用渲染或更新时,行不会被丢弃和重新 -每次创建时,这些都将被简单地重新排序,正如您在我的 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后面有一个名为 domdiff 的引擎,其目的是重新组织节点。

平均而言, domdiff中使用的算法非常快,但在某些情况下,它可能比一次创建相同布局的浏览器慢。

你可以很容易地在我的笔中发现,当你从 ASC 切换到 DESC 或反之亦然时,它比它的普通 DOM 对应物快 5 倍,但是当涉及到完全随机的有序列表时,domdiff做了很多检查 DOM对方根本不在乎,所以它可能会更慢。

简而言之,虽然 vanilla DOM 方法线性快(或慢),但算法一有最好的情况和最坏的情况。

在几乎所有情况下都表现良好的算法是 petit-dom 使用的算法,但整个逻辑部分的权重对于现实世界的场景来说是 IMO 不必要的,但对于非现实世界的基准测试来说肯定是令人印象深刻的。

70000行不费吹灰之力

这些天来,我正在研究一个超HTML自定义元素,其目的是通过一个可排序和可搜索的表格来处理数十万行,这已经不是什么秘密了。

你可以在这个页面中看到一个早期的原型,并意识到框架如何处理 10000 个表行并不重要,因为一个好的组件不应该在 DOM 上放置这么多节点,因为没有用户能够看到所有一下子。

正如您在那个 70K 表中看到的那样,排序是一瞬间,搜索和滚动也是如此,这就是HyperHTMLElement的全部荣耀。

这些基准?对于日常的、现实世界的任务来说不是那么有趣。

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

于 2018-09-07T08:05:54.190 回答