虚拟dom添加虚拟dom_虚拟DOM缓慢。 认识记忆化的DOM

虚拟dom添加虚拟dom

by Sindre Osen Aarsaether

通过Sindre Osen Aarsaether

虚拟DOM缓慢。 符合已记忆的DOM。 (The Virtual DOM is slow. Meet the Memoized DOM.)

超越虚拟DOM和状态管理 (Moving beyond the Virtual DOM and State Management)

The virtual DOM was a fantastic innovation. It brought about a much more productive way of writing web applications by allowing us to write our views in a declarative manner.

虚拟DOM是一项了不起的创新。 通过允许我们以声明的方式编写视图,它带来了一种更加高效的Web应用程序编写方式。

This big benefit has little to do with performance of the initial render. Instead, it is the process of updating the DOM to reflect changes in your state has become much faster.

这个很大的好处与初始渲染的性能无关。 相反,它是更新DOM以反映您状态变化的过程,变得更快。

This process of bringing the DOM in sync with the state is often referred to as DOM reconciliation.

使DOM与状态同步的过程通常称为DOM协调

If we had an infinitely fast reconciler, we could vastly simplify our applications by rendering everything on every single frame. The state layer would never need to know about views at all — much less send out events and track which views need to react when certain parts of the state change. The view would always be in sync with the data, no matter what you threw at it.

如果我们有一个无限快的协调器,则可以通过在每个帧上渲染所有内容来极大地简化我们的应用程序。 状态层根本不需要了解视图,更不用说发出事件并跟踪在状态的某些部分发生变化时需要响应的视图。 无论您对视图进行什么操作,视图都将始终与数据保持同步。

Sadly, virtual DOM implementations are not infinitely fast. They are, in fact, surprisingly slow. Thankfully, many have jumped on the Immutability™ bandwagon, in which case the virtual DOM thanks you! Others wrap all state in observables (e.g. mobx), and keep track of which view depends on what state. This allows you to reconcile only parts of your view, but comes with its own set of drawbacks.

令人遗憾的是,虚拟DOM实现并非无限快。 实际上,它们出奇地慢。 值得庆幸的是,许多人跃跃欲试,在这种情况下,虚拟DOM谢谢您! 其他人则将所有状态都包裹在可观察对象(例如mobx)中,并跟踪哪个视图取决于哪个状态。 这使您只能协调部分视图,但有其自身的缺点。

The biggest issue is that we tend to decide how to manage our application state based on our view layer. What if we could get better performance in a world where the data layer and view layer don’t really know or care about each other?

最大的问题是,我们倾向于根据视图层决定如何管理应用程序状态。 如果在一个数据层和视图层并不真正了解或关心彼此的世界中,我们可以获得更好的性能怎么办?

认识记忆化的DOM (Meet the Memoized DOM)

Imba is a programming language for the web. It powers the interactive screencasting platform scrimba.com, of which I am the lead developer. Imba was born to make developing web applications fun again. It features a clean and readable syntax inspired by Ruby. It compiles to readable and performant JavaScript, and works inside the existing ecosystem.

Imba是一种网络编程语言。 它为交互式截屏平台scrimba.com提供了动力 ,我是其中的首席开发人员。 Imba的诞生再次使开发Web应用程序变得有趣。 它具有受Ruby启发的简洁易读的语法。 它可以编译为可读和高性能JavaScript,并可以在现有的生态系统中运行。

Besides a clean and readable syntax, the biggest benefit of Imba is that it truly treats DOM elements as first-class citizens, on a much deeper level than JSX. It allows you to write views declaratively, yet it does not use a virtual DOM. Instead, Imba compiles views to a memoized DOM, which turns out to be an order of magnitude faster.

除了简洁易懂的语法外,Imba的最大好处是,它在比JSX更深的层次上真正将DOM元素视为一流的公民。 它允许您声明性地编写视图,但不使用虚拟DOM。 取而代之的是,Imba将视图编译为已记忆的DOM,事实证明它快了一个数量级

这个怎么运作 (How it works)

The general idea is that we create lightweight wrappers around DOM elements, and compile declarative views to chains of setters, each modifying the underlying DOM directly.

通常的想法是,我们围绕DOM元素创建轻量级包装,并将声明式视图编译为设置器链,每个视图都直接修改基础DOM。

tag AppView    def render        <self>            <h1.title> "Welcome"            <p.desc .red=(Math.random > 0.5)> "Roulette"

The Imba view above will roughly compile into the following javascript:

上方的Imba视图将大致编译为以下javascript:

class AppView extends Imba.Tag {  render() {    var $ = this.$; // inline cache for tag    return this.setChildren($.$ = $.$ || [      Imba.tag('h1',$).flag('title').setText("Welcome"),      Imba.tag('p',$).flag('desc').setText("Roulette")    ]).synced(      $[1].flagIf('red',Math.random() > 0.5)    );  }}

This is a very simple example to illustrate the basic concept. During compilation we split creation and updates into separate branches. The first time render is called for an <AppView> the children will be created and static attributes will be set. On all subsequent calls the only real work we do is flip the className of our <p>. Albeit much more complex, the same concept is used for conditionals, loops, and everything else inside tag trees.

这是一个非常简单的示例,用于说明基本概念。 在编译过程中,我们将创建和更新分为单独的分支。 第一次为<AppVi ew>调用渲染时,将创建子代并设置静态属性。 在所有后续调用,我们要做的唯一的真正的工作是翻转的classNameØ fo乌尔<P>。 尽管非常复杂,但相同的概念用于条件树,循环以及标签树中的所有其他内容。

If you’re interested in how it really works I recommend reading this intro.

如果您对它的实际工作方式感兴趣,建议阅读此介绍

基准测试 (Benchmark)

React is fast, they said. React is fast enough, they said. React Fiber will be fast enough, they said.
他们说,React很快。 他们说,React足够快。 他们说,React Fiber将足够快。

Most benchmarks test things like “insert/shuffle/remove 1000 rows”. This gives little indication about real-world performance. When there are hundres of changes, most of the difference is eaten up by actual DOM mutations, repainting, etc. It fails to measure the most important metric.

大多数基准测试都会测试“插入/随机播放/删除1000行”之类的内容。 这几乎没有提供有关实际性能的指示。 当存在数百种变化时,大多数差异会被实际的DOM突变,重绘等吞噬掉。它无法衡量最重要的指标。

If you truly want to test the performance of DOM reconciliation, you need to look at how quickly the implementation brings the DOM in sync with the state, especially when there are few/no changes.

如果您确实想测试DOM协调的性能,则需要查看实现使DOM与状态同步的速度, 尤其是在更改很少/没有更改的情况下

So, to capture a realistic view of the reconciler performance, we could change a small part of the application state in each iteration, and then measure the time it takes to forcefully bring the view in sync with this changed state. The view should not be listening to any part of the state, and the state should not need to notify anyone whether it has changed.

因此,为了捕获协调器性能的真实视图,我们可以在每次迭代中更改应用程序状态的一小部分,然后测量将视图强制与此更改状态同步所需的时间。 该视图不应监听状态的任何部分,并且该状态不需要通知任何人它是否已更改。

This benchmark steps through a deterministic sequence of state alterations, doing at most one change per iteration. We are measuring the time it takes to reconcile the whole application view after:

该基准测试逐步执行确定性的状态更改序列, 每次迭代最多进行一次更改 。 在以下情况下,我们正在测量协调整个应用程序视图所需的时间:

  1. Toggling the completion of a task

    切换任务的完成
  2. Removing a task

    删除任务
  3. Inserting a task

    插入任务
  4. Renaming a task

    重命名任务
  5. Doing nothing

    什么也不做

结果 (Results)

Running the benchmark on an iMac (4GHz i7) yields the following results:

在iMac(4GHz i7)上运行基准测试会产生以下结果:

Safari 11 (Safari 11)
  • Imba 1.3: 360458 ops / sec

    Imba 1.3: 每秒360458次操作

  • React 16.2: 9752 ops / sec — 36.96x slower

    React 16.2: 9752操作/秒- 慢36.96倍

  • Vue 2.5: 8719 ops / sec — 41.34x slower

    Vue 2.5:每秒8719次操作- 慢41.34倍

Chrome65 (Chrome 65)
  • Imba 1.3: 282484 ops / sec

    Imba 1.3: 282484次操作/秒

  • React 16.2: 8882 ops / sec — 31.81x slower

    React 16.2: 8882次操作/秒- 慢31.81倍

  • Vue 2.5: 8103 ops / sec — 34.86x slower

    Vue 2.5:每秒8103次操作- 慢34.86倍

Firefox 58 (Firefox 58)
  • Imba 1.3: 234334 ops / sec

    Imba 1.3: 每秒234334次操作

  • React 16.2: 5075 ops / sec — 46.17x slower

    React 16.2: 5075次操作/秒- 慢46.17倍

  • Vue 2.5: 3119 ops / sec — 75.13x slower

    Vue 2.5:每秒3119次操作- 慢75.13倍

This seems outrageous right? Surely, it cannot be right.

这似乎太离谱了吧? 当然,这是不对的。

  • All implementations are really reconciling on every step.

    所有实现真正和解的每一步。

  • All implementations are blocking, synchronous, and deterministic.

    所有实现都是阻塞,同步和确定性的。
  • All implementations are performing the same amount of DOM mutations.

    所有实现都执行相同数量的DOM突变。
  • Yes, we are using the minified production build of React. The development version is 200x slower than Imba on the same test.

    是的,我们使用的是最小化的React生产版本。 在同一测试中,开发版本比Imba 慢200倍。

  • The memoized DOM creates practically no garbage during an iteration, uses less memory overall, and is conceptually very simple.

    备注DOM在迭代过程中几乎不产生垃圾,总体上使用较少的内存,并且从概念上讲非常简单。

All the implementations can probably be optimized more. I’m very happy to accept pull-requests at GitHub. To be clear, I have tremendous respect for what React has achieved, and I truly love Vue. Imba has taken a lot of inspiration from it. I suspect it should be possible to compile Vue templates using a similar approach, and would love for someone to give it a go!

所有实现可能都可以进行更多优化。 我很高兴在GitHub接受请求请求。 明确地说,我对React所取得的成就深表敬意,并且我真的很喜欢Vue。 Imba从中汲取了很多灵感。 我怀疑应该可以使用类似的方法来编译Vue模板,并且希望有人可以尝试一下!

剖析 (Profiling)

Let’s test the raw reconciler performance when there aren’t even any changes. This removes the time spent doing actual DOM mutations from the equation, and gives us a good picture about how much work is going on during reconciliation. The charted CPU profile from Chrome gives a visual indication of how much less work is done with the memoized DOM technique.

当没有任何变化时,让我们测试原始调节器性能。 这消除了从等式中进行实际DOM突变所花费的时间,并为我们提供了有关协调期间正在进行的工作量的清晰图片。 Chrome的图表CPU配置文件直观显示了使用DOM技术的工作量。

英巴1.3 (Imba 1.3)
React16.2 (React 16.2)
Vue 2.5 (Vue 2.5)

可以缩放吗? (Does it scale?)

“There are A LOT, and I mean, A LOT of small little projects that claim more speed, easier development, but on closer inspection usually lack very important features (such as module life cycle hooks) and, of course without them the performance is higher, but the flexibility to use those libraries beyond a todo list application is limited.”
“有很多,我的意思是,很多小的项目要求更快,更轻松的开发,但是仔细检查通常会缺少非常重要的功能(例如模块生命周期挂钩),当然,如果没有它们,性能是更高,但是在待办事项列表应用程序之外使用这些库的灵活性受到限制。”

This is a quote from someone who read through an early draft of this article, and I would like to tackle it head on. The performance difference is not limited to a simple test, quite the contrary. Imba has been used in production for several years at scrimba.com, but it is still not for the faint of heart. For most developers the massive ecosystems for Vue and React will be hard (and probably unwise) to leave behind. The Imba documentation still leaves a lot to be desired, but we are improving it every day.

这是从阅读本文初稿的人那里引用的,我想直接解决。 相反,性能差异不仅限于简单的测试。 Imba已在scrimba.com上用于生产多年,但仍不是出于胆小。 对于大多数开发人员而言,Vue和React的庞大生态系统将很难(可能不明智)落在后面。 Imba文档仍然有很多不足之处,但是我们每天都在改进它。

有关系吗? (Does it matter?)

I’m sure you’ve heard that React is fast enough. But fast enough for what? It doesn’t really matter if React was 15% faster, but with an order of magnitude improvement we can start to explore simpler ways to build applications.

我确定您已经听说过React足够快。 但是足够快吗? React是否快15%并不重要,但是随着数量级的提高,我们可以开始探索构建应用程序的更简单方法。

It’s not about the perceived speed, but about what it lets you do. At scrimba.com we don’t worry about keeping the view in sync with the state. We don’t worry about tracking when state has changed. Our data models are not observable. We just render. Whenever. And it’s liberating.

这与感知的速度无关,而与它可以做什么相关。 在scrimba.com,我们不必担心视图与状态保持同步。 我们不必担心状态更改的时间。 我们的数据模型是不可观察的。 我们只是渲染。 每当。 它正在解放。

翻译自: https://www.freecodecamp.org/news/the-virtual-dom-is-slow-meet-the-memoized-dom-bb19f546cc52/

虚拟dom添加虚拟dom

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值