Svelte在吹,或许有道理,虚拟DOM纯属是一种开销(翻译版本)

如果您在最近几年中使用过JavaScript框架,那么您可能已经听说过“虚拟DOM快速”这一短语,通常被认为是它比真实DOM更快。 这是一个令人惊讶的弹性模因-例如,人们问Svelte在不使用虚拟DOM时如何快速

什么是虚拟DOM?

在许多框架中,你可以使用使用 render()函数,来构建APP,例如 react 组件

function HelloMessage(props) {
  return (
    <div className="greeting">
      Hello {props.name}
    </div>
  );
}

没有JSX,你也可以做到

function HelloMessage(props) {
  return React.createElement(
    ‘div‘,
    { className: ‘greeting‘ },
    ‘Hello ‘,
    props.name
  );
}

但是结果是一样的-一个代表页面现在外观的对象。 该对象是虚拟DOM。 每当您的应用程序状态更新时(例如,name 属性 更改时),您都会创建一个新的状态。 框架的工作是将新的与旧的进行协调,以找出需要进行哪些更改并将其应用于真实的DOM

关于虚拟DOM性能的误解可追溯到React的发布。在前思想核心团队成员皮特·亨特(Pete Hunt)2013年的开创性演讲《重新思考最佳实践》中,我们学到了以下内容

https://www.youtube.com/watch?v=x7cQ3mrcKaY
?
但是,虚拟DOM操作是对实际DOM的最终操作的补充,唯一可能更快的方法是,如果我们将其与效率较低的框架进行比较(2013年还有很多事情要做!),或反对一个稻草人-替代方案是做某事,而实际上没有人做:

onEveryStateChange(() => {
  document.body.innerHTML = renderMyApp();
});

react不是魔术。 就像您可以使用C进入汇编器并击败C编译器一样,您也可以进入原始DOM操作和DOM API调用,并在需要时击败React。 但是,使用C或Java或JavaScript可以将性能提高一个数量级,因为您不必担心…平台的细节。 使用React,您甚至可以不考虑性能而构建应用程序,并且默认状态是快速的。

所以…虚拟DOM慢吗
不完全是。它更像是“虚拟DOM通常足够快”,但有一些警告。
React的最初承诺是,您可以在每个状态更改时重新渲染整个应用程序,而不必担心性能。 实际上,我认为事实并非如此。 如果是这样,就不需要像shouldComponentUpdate这样的优化(这是一种告诉React何时可以安全地跳过组件的方法)
即使使用shouldComponentUpdate,一次完成更新整个应用程序的虚拟DOM仍需要大量工作。 不久前,React团队引入了一种叫做React Fiber的东西,它可以将更新分成较小的块。 这意味着(除其他事项外)更新不会长时间阻塞主线程,尽管它不会减少工作总量或更新所花费的时间

开销来自哪里?

最明显的是,差异并非免费。您必须先将新的虚拟DOM与先前的快照进行比较,然后才能对真实DOM应用更改。以较早的HelloMessage示例为例,假设名称prop从“ world”更改为“ everybody”

两个快照都包含一个元素。在两种情况下都是

,这意味着我们可以保留相同的DOM节点

?

我们枚举旧的

和新的属性,以查看是否需要更改,添加或删除任何属性。在这两种情况下,我们都有一个属性-值为“ greeting”的className

?

下降到元素中,我们看到文本已更改,因此我们需要更新真实的DOM

在这三个步骤中,只有第三个步骤在这种情况下才有价值,因为-就像绝大多数更新一样-应用程序的基本结构没有改变。如果我们直接跳到步骤3,效率会更高:

if (changed.name) {
  text.data = name;
}

(这几乎完全是Svelte生成的更新代码。与传统的UI框架不同,Svelte是一种编译器,它在构建时就知道应用程序中的情况如何变化,而不必等着在运行时完成工作。)
虽然不只是差异

React和其他虚拟DOM框架使用的差异算法速度很快。可以说,组件本身的开销更大。你不会写这样的代码…

function StrawManComponent(props) {
  const value = expensivelyCalculateValue(props.foo);

  return (
    <p>the value is {value}</p>
  );
}

因为无论props.foo是否已更改,您都会在每次更新时不小心重新计算值。但是以似乎更有益的方式进行不必要的计算和分配是非常普遍的:

function MoreRealisticComponent(props) {
  const [selected, setSelected] = useState(null);

  return (
    <div>
      <p>Selected {selected ? selected.name : ‘nothing‘}</p>

      <ul>
        {props.items.map(item =>
          <li>
            <button onClick={() => setSelected(item)}>
              {item.name}
            </button>
          </li>
        )}
      </ul>
    </div>
  );
}

在这里,我们会在每次状态更改时生成一个新的虚拟

  • 元素数组-每个元素都有自己的内联事件处理程序-不管props.items是否已更改。 除非您对性能不满意,否则不会对其进行优化。 毫无意义。 它足够快。 但是你知道会更快吗? 不这样做。
  • ?

    即使进行琐碎的工作,也默认不执行不必要的工作的危险在于,您的应用最终将屈服于“千刀相砍”,而没有明显的瓶颈可以立即优化。

    Svelte的设计明确可以防止您陷入这种情况

    为什么框架使用虚拟DOM

    了解虚拟DOM不是一项功能很重要。 这是达到目的的一种手段,最终是声明性的,状态驱动的UI开发。 虚拟DOM非常有价值,因为它使您无需考虑状态转换即可构建应用程序,并且性能通常足够好。 这意味着更少的错误代码,而将更多的时间花费在创造性的任务上,而不是乏味的工作上。

    但是事实证明,我们无需使用虚拟DOM就可以实现类似的编程模型-这就是Svelte的用武之地

?

Svelte在吹,或许有道理,虚拟DOM纯属是一种开销(翻译版本)

上一篇:程序中如何设计backup request功能


下一篇:Vue基础