Virtual DOM 简直就是挥霍
彻底澄清“Virtual DOM 飞快”的神话。
注意:原文发表于2018-12-27,随着框架不断演进,部分内容可能已不适用。
近年来,如果你有使用过 JavaScript 框架,那么你可能听说过“Virtual DOM 飞快”,甚至认为比真实的 DOM 还要快。
令人震惊的是,这种说法竟然深入人心。
有人曾问我 Svelte 不使用 Virtual DOM,它为何更快?看来现在是时候仔细探讨一下。
什么是 Virtual DOM?
在众多框架中,你通常是使用 render()
函数来构建应用程序UI的,就像下方这个简单的 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
);
}
……后者正是前者的宗本道源,其结果自然一致:代表的是页面渲染的对象。
这个对象就是 Virtual DOM。
一旦程序更新了状态(例如 name
属性被修改),便会创建新的对象。
框架要做的工作是对比新旧对象之间的差异,找出需要进行重新渲染的部分,并将其应用到真实的 DOM 中。
这种观念是如何开始的?
关于 Virtual DOM 性能的误解,可以追溯到 React 正式发布那会。
在2013年,React 前团队核心成员 Pete Hunt 在《重新思考最佳实践》的演讲中提到:
这确实是快如闪电,主要是因为大多数 DOM 操作慢如蜗牛, DOM 有很多性能上的开销,大多数 DOM 操作往往会掉链子。
截图来自 JSConf EU 2013 《重新思考最佳实践》
但是 —— 慢着!
Virtual DOM 只是真实 DOM 操作锦上添花的补充而已。
它之所以快,是因为拿性能更差的框架做对比(在2013年,可以欺负的选择有很多!),另一种选择是做一些他人不屑去做的事情:
onEveryStateChange(() => {
document.body.innerHTML = renderMyApp();
});
Pete 很快就澄清……
React 不是魔法。
就像你可以使用C进入汇编程序并击溃C编译器一样,你可以进入原生 DOM 操作和 DOM API 调用,并在时机来临时击溃 React。
然而,使用 C、Java 或者 JavaScript,可以将性能提升一个数量级,你不必担心……平台的细节。
使用 React,你可以构建应用程序时无需顾及性能问题,它本身就很快。
…… 但这还是没有挠到痒处。
那么……Virtual DOM 慢吗?
并不尽然。
如果能够防患于未然,那确实“Virtual DOM 飞快”。
React 最初的承诺是,你可以在每次状态改变时,自动重新渲染你的整个应用,且不用担心性能。
不敢苟同。
果真如此,那就不需要像 shouldComponentUpdate
这样的优化了(这是一个用于告诉 React 何时可以安全地跳过一个组件的方法)。
就算用了 shouldComponentUpdate
,一次性更新整个应用的 Virtual DOM 也大费周折。
前不久 React 团队引入一种叫 React Fiber 的东西,它可以将更新划分成较小的块。
这意味着(除了其他事项外)更新不会长时间阻塞主线程,尽管它不会减少工作总量或总体耗时。
开销从何而来?
显而易见,DOM差异比较(diffing)并非毫无代价。
这必须先将新的 Virtual DOM 与旧的差异(快照)进行比较,然后才能对真实 DOM 应用更改。
就拿前面的 HelloMessage
为例,假设 name
属性从“world”更改为“everybody”:
两个快照都包含一个元素,在这种情况下,它都是
<div>
,这意味着我们可以保持相同的 DOM 节点。我们枚举
<div>
旧的和新的所有属性,以查看是否需要更改、添加或者删除任何属性。在这两种情况下,我们都有一个特性,就是它的值为“greeting
”的类名
。扫描元素内部,我们看到文本已经更改了,因此我们需要更新实际的 DOM。
在上述三步里,只有第3步在该示例中有价值,因为程序的基本结构是没有改变的,这其实在绝大多数的更新中都是如此。
如果我们直接跳到第3步,效率就高得多了:
if (changed.name) {
text.data = name;
}
(这几乎就是 Svelte 生成的更新代码了。与传统的 UI 框架有所不同,Svelte 是一个编译器,它可以在构建时便知悉程序中可能发生的变化,而非运行时。)
不止差异比较一个方面
React 和其他 Virtual DOM 框架使用的 diffing 算法速度都很快。
换而言之,组件本身的开销更大。
例如你不太可能会写出这样的代码……
function StrawManComponent(props) {
const value = expensivelyCalculateValue(props.foo);
return (
<p>the value is {value}</p>
);
}
如果这么干,无论 props.foo
是否已经更改,你可能会粗心地在每次更新时不小心重新计算了 value
。
不过,对于进行不必要的计算和分配,更普遍的是下面这种方式:
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
是否发生了变化。
除非你对性能有所不满,否则就不会对其进行优化。这么干毫无意义,它足够快了。
但是你想知道怎样会更快吗?那是在浪费时间。
其实,默认就做一些不必要的计算(即使是微不足道的),其危险之处在于你的应用最终会温水煮青蛙般死掉,因为没有明显的瓶颈值得去优化。
React Hooks 会使情况变本加厉,结果可想而知。
Svelte 专门设计用来防止你陷入这种困境。
那些框架为何还要用 Virtual DOM?
关键是要理解:Virtual DOM 不是一种特性,而是一种手段。
它要达到的目的是支持声明式的、状态驱动的 UI 开发。
Virtual DOM 很有价值,因为它允许你在构建应用程序时无需考虑状态转换,而且性能通常_已经足够好了_。
这意味着更少的错误代码,更多的时间花在创造性的任务上,而不是在单调乏味的地方折腾。
但事实证明,我们可以在不使用 Virtual DOM 的情况下实现类似的编程模型 —— 这正是 Svelte 的用武之地。
< The End >
Virtual DOM 简直就是挥霍的更多相关文章
- 窥探Vue.js 2.0 - Virtual DOM到底是个什么鬼?
引言 你可能听说在Vue.js 2.0已经发布,并且在其中新添加如了一些新功能.其中一个功能就是"Virtual DOM". Virtual DOM是什么 在之前,React和Em ...
- 个人对于Virtual DOM的一些理解
之前一直认为react的Virtual DOM操作会比传统的操作DOM要快,这其实是错误的,React 从来没有说过 "React 比原生操作 DOM 快".如果没有 Virtua ...
- 抛开react,如何理解virtual dom和immutability
去年以来,React的出现为前端框架设计和编程模式吹来了一阵春风.很多概念,无论是原本已有的.还是由React首先提出的,都因为React的流行而倍受关注,成为大家研究和学习的热点.本篇分享主要就聚焦 ...
- 深度剖析:如何实现一个 Virtual DOM 算法
本文转载自:https://github.com/livoras/blog/issues/13 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步 ...
- Virtual DOM 算法
前端 virtual-dom react.js javascript 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步骤一:用JS对象模拟DOM ...
- React v16-alpha 从virtual dom 到 dom 源码简读
一.物料准备 1.克隆react源码, github 地址:https://github.com/facebook/react.git 2.安装gulp 3.在react源码根目录下: $npm in ...
- React源码解析-Virtual DOM解析
前言:最近一直在研究React,看了陈屹先生所著的深入React技术栈,以及自己使用了这么长时间.对React应该说有比较深的理解了,正好前阵子也把两本关于前端设计模式的书看完了,总感觉有一种知识错综 ...
- virtual dom的实践
最近基于virtual dom 写了一个小框架-aoy. aoy是一个轻量级的mvvm框架,基于Virtual DOM.虽然现在看起来很单薄,但我做了完善的单元测试,可以放心使用.aoy的原理可以说和 ...
- 深度理解 Virtual DOM
目录: 1 前言 2 技术发展史 3 Virtual DOM 算法 4 Virtual DOM 实现 5 Virtual DOM 树的差异(Diff算法) 6 结语 7 参考链接 1 前言 我会尽量把 ...
随机推荐
- mysql高级day4
Mysql高级-day04 1. MySql中常用工具 1.1 mysql 该mysql不是指mysql服务,而是指mysql的客户端工具. 语法 : mysql [options] [databas ...
- 深入浅出Java线程池:源码篇
前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...
- 翻译:《实用的Python编程》00_Setup
课程设置与概述 欢迎访问本课程(Practical Python Programming).这个页面包含一些关于课程设置的重要信息. 课程周期和时间要求 该课程最初是作为一个由讲师主导的,持续 3 - ...
- 树与图的DFS与BFS
树的DFS 题目:https://www.acwing.com/problem/content/848/ 代码 #include<bits/stdc++.h> using namespac ...
- H - 看病要排队
看病要排队这个是地球人都知道的常识.不过经过细心的0068的观察,他发现了医院里排队还是有讲究的.0068所去的医院有三个医生(汗,这么少)同时看病.而看病的人病情有轻重,所以不能根据简单的先来先服务 ...
- 2013 Asia Hangzhou Regional Contest hdu4780 Candy Factory
参考:https://blog.csdn.net/sd_invol/article/details/15813671 要点 每个任务的结束时间是固定的,不受任何因素影响 机器只在最一开始有用,在那之后 ...
- 【uva 1615】Highway(算法效率--贪心 区间选点问题)
题意:给定平面上N个点和一个值D,要求在x轴上选出尽量少的点,使得对于给定的每个店,都有一个选出的点离它的欧几里德距离不超过D. 解法:先把问题转换成模型,把对平面的点满足条件的点在x轴的直线上可得到 ...
- Git 初始化及仓库创建及操作
一.基本信息设置 1.初始化设置用户名 2.初始化设置用户名邮箱 备注:该设置在Github仓库主页显示谁提交了该文件. 二.初始化一个新的Git仓库 1.创建文件夹 mkdir test 2.在文件 ...
- Postman查看上传文件过程时出现400 - Required MultipartFile parameter 'files' is not present错误
我在利用postman查看上传图片文件时,出现了如下图的错误,看到之后很懵逼. 图1 上网搜了一下,归结下来就是参数不一致导致的.不过还有一些是由于没加注解,如下图示: 图2 关于参数不一致问题,主要 ...
- 在竞赛中使用new的问题
问了一下KingSann大佬,大佬说 找空闲内存均摊O(1)但是如果new多了就是O(n) 真tm可怕..还是开个内存池好了.. 要么直接now++,要么直接Node *s=&node[t ...