前端: 虚拟 DOM 是纯粹的开销

2023-01-04 0 363

让他们十分困难地消解“交互式DOM迅速”的神话故事

假如你在往后一两年中采用过JavaScript架构,你可能将听闻过“交互式DOM迅速”这句话,一般来说意味著它比或者说的DOM更快。这是两个更让人吃惊的灵活性迷因——比如,现代人问 Svelte 在不采用交互式 DOM 的情况下怎样加速。

是这时候细细看一看了。

甚么是交互式 DOM?

在很多架构中,您能透过建立表达式来构筑插件,比如render()化学反应组件:

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。每天应用领域的状况预览时(比如,当人偶更动时),您单厢建立两个新状况。架构的工作是协同新架构与旧架构,找寻必要性的更动并将其应用领域于或者说的 DOM。name

这个迷因是怎样开始的?

关于交互式 DOM 性能的误解能追溯到 React 的发布。在重新思考最佳实践,前 React 核心团队成员 Pete Hunt 在 2013 年的一次开创性演讲中,他们学到了以下内容:

这实际上非常快,主要是因为大多数 DOM 操作往往很慢。DOM 上有很多性能组织工作,但大多数 DOM 操作倾向于丢帧。

前端: 虚拟 DOM 是纯粹的开销

截图来自重新思考最佳实践在JSConfEU 2013

但是等一下!交互式 DOM 操作是对真实 DOM 上的最终操作的补充。它更快的唯一方法是,假如他们将其与效率较低的架构进行比较(2013年有很多事要做!),或者反对稻草人 – 另一种选择是做一些没人或者说做的事:

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

皮特不久后澄清…

化学反应不是魔法。就像你能采用 C 进入汇编程序并击败 C 编译器一样,假如你愿意,你能进入原始 DOM 操作和 DOM API 调用并击败 React。但是,采用 C 或 Java 或 JavaScript 是性能的两个数量级改进,因为您不必担心……关于平台的细节。采用 React,您能构筑插件,甚至不考虑性能,默认状况迅速。

…但这不是卡住的部分。

所以。。。交互式 DOM很慢吗?

不完全是。它更像是“交互式 DOM 一般来说足够快”,但有一些警告。

React 最初的承诺是,你能在每天状况更动时重新渲染整个插件,而不必担心性能。在实践中,我认为这并不准确。假如是这样,就不需要像这样的优化(这是一种告诉 React 何时能安全地跳过组件的方式)。shouldComponentUpdate

即使采用 ,一次性预览整个插件的交互式 DOM 也是很多组织工作。不久前,React 团队推出了一种叫做 React Fiber 的东西,它允许将预览分解成更小的块。这意味著(除其他事项外)预览不会长时间阻塞主线程,尽管它不会减少总组织工作量或预览所需的时间。shouldComponentUpdate

开支从何而来?

最明显的是,差异不是免费的.假如不先将新的交互式 DOM 与以前的快照进行比较,则无法将更动应用领域于实际 DOM。举前面的例子,假设人偶从“世界”变成了“每个人”。HelloMessagename

两个快照都包含两个元素。在这两种情况下,它都是两个 ,这意味著他们能保留完全相同的 DOM 节点<div>他们枚举旧属性和新属性上的所有属性,以查看是否需要更动、添加或删除任何属性。在这两种情况下,他们都有两个属性 — 值为<div>className”greeting”下降到元素,他们看到文本已经改变,所以他们需要预览或者说的 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> ); }

…因为您会粗心地重新计算每天预览,无论是否已更动。但是,以看起来更良性的方式进行不必要性的计算和分配是极其常见的:valueprops.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> ); }

在这里,他们将在每天状况更动时生成两个新的交互式元素数组 – 每个元素都有自己的内联事件处理程序,无论是否已更动。除非你不健康地痴迷于性能,否则你不会优化它。没意义。它足够快了。但是你知道甚么会更快吗?不这样做。<li>props.items

化学反应钩子默认做不必要性的组织工作加倍努力,与可预测的结论.

默认做不必要性的组织工作的危险,即使这项组织工作是微不足道的,是你的插件最终会屈服于“千刀万剐”,没明确的瓶颈能瞄准,一旦需要优化。

Svelte明确设计用于防止您陷入这种情况。

那么为甚么架构采用交互式 DOM?

重要的是要了解交互式 DOM 不是一项功能。它是达到目的的一种手段,目的就是声明性的、状况驱动的 UI 开发。交互式 DOM 很有价值,因为它允许您在不考虑状况转换的情况下构筑插件,并且性能一般来说足够好。这意味著更少的错误代码,更多的时间花在创造性任务上,而不是乏味的任务上。

但事实证明,他们能在不采用交互式 DOM 的情况下实现类似的编程模型——这就是 Svelte 的用武之地。

里奇·哈里斯 12月 27 2018

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务