React虚拟DOM的理解
React虚拟DOM的理解
Virtual DOM
是一棵以JavaScript
对象作为基础的树,每一个节点可以将其称为VNode
,用对象属性来描述节点,实际上它是一层对真实DOM
的抽象,最终可以通过渲染操作使这棵树映射到真实环境上,简单来说Virtual DOM
就是一个Js
对象,是更加轻量级的对DOM
的描述,用以表示整个文档。
描述
在浏览器中构建页面时需要使用DOM
节点描述整个文档。
<div class="root" name="root">
<p>1</p>
<div>11</div>
</div>
如果使用Js
对象去描述上述的节点以及文档,那么便类似于下面的样子。
{
type: "tag",
tagName: "div",
attr: {
className: "root"
name: "root"
},
parent: null,
children: [{
type: "tag",
tagName: "p",
attr: {},
parent: {} /* 父节点的引用 */,
children: [{
type: "text",
tagName: "text",
parent: {} /* 父节点的引用 */,
content: "1"
}]
},{
type: "tag",
tagName: "div",
attr: {},
parent: {} /* 父节点的引用 */,
children: [{
type: "text",
tagName: "text",
parent: {} /* 父节点的引用 */,
content: "11"
}]
}]
}
React中的虚拟DOM
Virtual DOM
是一种编程概念,在这个概念里,UI
以一种理想化的,或者说虚拟的表现形式被保存于内存中,并通过如ReactDOM
等类库使之与真实的DOM
同步,这一过程叫做协调。这种方式赋予了React
声明式的API
,您告诉React
希望让UI
是什么状态,React
就确保DOM
匹配该状态,这样可以从属性操作、事件处理和手动DOM
更新这些在构建应用程序时必要的操作中解放出来。
与其将Virtual DOM
视为一种技术,不如说它是一种模式,人们提到它时经常是要表达不同的东西。在React
的世界里,术语Virtual DOM
通常与React
元素关联在一起,因为它们都是代表了用户界面的对象,而React
也使用一个名为fibers
的内部对象来存放组件树的附加信息,上述二者也被认为是React
中Virtual DOM
实现的一部分。
React中的虚拟DOM的历史
在之前,Facebook
是PHP
大户,所以React
最开始的灵感就来自于PHP
。
在2004
年这个时候,大家都还在用PHP
的字符串拼接来开发网站。
$str = "<ul>";
foreach ($talks as $talk) {
$str += "<li>" . $talk->name . "</li>";
}
$str += "</ul>";
这种方式代码写出来不好看不说,还容易造成XSS
等安全问题。应对方法是对用户的任何输入都进行转义Escape
,但是如果对字符串进行多次转义,那么反转义的次数也必须是相同的,否则会无法得到原内容,如果又不小心把HTML
标签给转义了,那么HTML
标签会直接显示给用户,从而导致很差的用户体验。
到了2010
年,为了更加高效的编码,同时也避免转义HTML
标签的错误,Facebook
开发了XHP
。XHP
是对PHP
的语法拓展,它允许开发者直接在PHP
中使用HTML
标签,而不再使用字符串。
$content = <ul />;
foreach ($talks as $talk) {
$content->appendChild(<li>{$talk->name}</li>);
}
这样的话,所有HTML
标签都使用不同于PHP
的语法,我们可以轻易的分辨哪些需要转义哪些不需要转义。不久的后来,Facebook
的工程师又发现他们还可以创建自定义标签,而且通过组合自定义标签有助于构建大型应用。
到了2013
年,前端工程师Jordan Walke
向他的经理提出了一个大胆的想法:把XHP
的拓展功能迁移到Js
中,首要任务是需要一个拓展来让JS
支持XML
语法,该拓展称为JSX
。因为当时由于Node.js
在Facebook
已经有很多实践,所以很快就实现了JSX
。
const content = (
<TalkList>
{talks.map(talk => <Talk talk={talk} />)}
</TalkList>
);
在这个时候,就有另外一个很棘手的问题,那就是在进行更新的时候,需要去操作DOM
,传统 DOM API
细节太多,操作复杂,所以就很容易出现Bug
,而且代码难以维护。然后就想到了PHP
时代的更新机制,每当有数据改变时,只需要跳到一个由PHP
全新渲染的新页面即可。
从开发者的角度来看的话,这种方式开发应用是非常简单的,因为它不需要担心变更,且界面上用户数据改变时所有内容都是同步的。为此React
提出了一个新的思想,即始终整体刷新页面,当发生前后状态变化时,React
会自动更新UI
,让我们从复杂的UI
操作中解放出来,使我们只需关于状态以及最终UI
长什么样。这个时候,我只需要关系我的状态(数据是什么),以及UI
长什么样(布局),不再需要关系操作细节。
这种方式虽然简单粗暴,但是很明显的缺点,就是很慢。另外还有一个问题就是这样无法包含节点的状态,比如它会失去当前聚焦的元素和光标,以及文本选择和页面滚动位置,这些都是页面的当前状态。
为了解决上面说的问题,对于没有改变的DOM
节点,让它保持原样不动,仅仅创建并替换变更过的DOM
节点,这种方式实现了DOM
节点复用Reuse
。至此,只要能够识别出哪些节点改变了,那么就可以实现对DOM
的更新,于是问题就转化为如何比对两个DOM
的差异。说到对比差异,可能很容易想到版本控制git
。DOM
是树形结构,所以diff
算法必须是针对树形结构的,目前已知的完整树形结构的编辑距离diff
算法复杂度为O(n^3)
。但是时间复杂度O(n^3)
太高了,所以Facebook
工程师考虑到组件的特殊情况,进行了一些优化与折中,然后将复杂度降低到了O(n)
。
DOM
是复杂的,对它的操作尤其是查询和创建是非常慢非常耗费资源的。看下面的例子,仅创建一个空白的div
,其实例属性就达到294
个。
// Chrome v84
const div = document.createElement("div");
let m = 0;
for (let k in div) m++;
console.log(m); // 294
对于DOM
这么多属性,其实大部分属性对于做Diff
是没有任何用处的,所以如果用更轻量级的Js
对象来代替复杂的DOM
节点,然后把对DOM
的diff
操作转移到Js
对象,就可以避免大量对DOM
的查询操作。这个更轻量级的Js
对象就称为Virtual DOM
。那么现在的过程就是这样:
- 维护一个使用
Js
对象表示的Virtual DOM
,与真实DOM
一一对应。 - 对前后两个
Virtual DOM
做diff
,生成变更Mutation
。 - 把变更应用于真实
DOM
,生成最新的真实DOM
。
可以看出,因为要把变更应用到真实DOM
上,所以还是避免不了要直接操作DOM
,但是React
的diff
算法会把DOM
改动次数降到最低。关于React
中的虚拟DOM
创建过程可以参考https://github.com/facebook/react/blob/9198a5cec0936a21a5ba194a22fcbac03eba5d1d/packages/react/src/ReactElement.js#L348
。
总结
传统前端的编程方式是命令式的,直接操纵DOM
,告诉浏览器该怎么干,这样的问题就是,大量的代码被用于操作DOM
元素,且代码可读性差,可维护性低。React
的出现,将命令式变成了声明式,摒弃了直接操作DOM
的细节,只关注数据的变动,DOM
操作由框架来完成,从而大幅度提升了代码的可读性和可维护性。
在初期我们可以看到,数据的变动导致整个页面的刷新,这种效率很低,因为可能是局部的数据变化,但是要刷新整个页面,造成了不必要的开销。所以就有了Diff
过程,将数据变动前后的DOM
结构先进行比较,找出两者的不同处,然后再对不同之处进行更新渲染。但是由于整个DOM
结构又太大,所以采用了更轻量级的对DOM
的描述—虚拟DOM
。
不过需要注意的是,虚拟DOM
和Diff
算法的出现是为了解决由命令式编程转变为声明式编程、数据驱动后所带来的性能问题的。换句话说,直接操作DOM
的性能并不会低于虚拟DOM
和Diff
算法,甚至还会优于。框架的意义在于为你掩盖底层的DOM
操作,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护,没有任何框架可以比纯手动的优化DOM
操作更快,因为框架的DOM
操作层需要应对任何上层API
可能产生的操作,它的实现必须是普适的。
虚拟DOM优缺点
优点
Virtual DOM
在牺牲(牺牲很关键)部分性能的前提下,增加了可维护性,这也是很多框架的通性。- 实现了对
DOM
的集中化操作,在数据改变时先对虚拟DOM
进行修改,再反映到真实的DOM
,用最小的代价来更新DOM
,提高效率。 - 打开了函数式
UI
编程的大门。 - 可以渲染到
DOM
以外的端,使得框架跨平台,比如ReactNative
,React VR
等。 - 可以更好的实现
SSR
,同构渲染等。 - 组件的高度抽象化。
缺点
- 首次渲染大量
DOM
时,由于多了一层虚拟DOM
的计算,会比innerHTML
插入慢。 - 虚拟
DOM
需要在内存中的维护一份DOM
的副本,多占用了部分内存。 - 如果虚拟
DOM
大量更改,这是合适的。但是单一的、频繁的更新的话,虚拟DOM
将会花费更多的时间处理计算的工作。所以如果你有一个DOM
节点相对较少页面,用虚拟DOM
,它实际上有可能会更慢,但对于大多数单页面应用,这应该都会更快。
每日一题
https://github.com/WindrunnerMax/EveryDay
参考
https://zhuanlan.zhihu.com/p/99973075
https://www.jianshu.com/p/e0a3ac85db5c
https://www.jianshu.com/p/9a1d2750457f
https://github.com/livoras/blog/issues/13
https://juejin.cn/post/6844904165026562056
https://juejin.cn/post/6844903640512086029
https://zh-hans.reactjs.org/docs/faq-internals.html#what-is-the-virtual-dom
React虚拟DOM的理解的更多相关文章
- React虚拟DOM浅析
在Web开发中,需要将数据的变化实时反映到UI上,这时就需要对DOM进行操作,但是复杂或频繁的DOM操作通常是性能瓶颈产生的原因,为此,React引入了虚拟DOM(Virtual DOM)的机制. 什 ...
- React虚拟DOM具体实现——利用节点json描述还原dom结构
前两天,帮朋友解决一个问题: ajax请求得到的数据,是一个对象数组,每个对象中,具有三个属性,parentId,id,name,然后根据这个数据生成对应的结构. 刚好最近在看React,并且了解到其 ...
- react虚拟dom diff算法
react虚拟dom:依据diff算法 前端:更新状态.更新视图:所以前端页面的性能问题主要是由Dom操作引起的,解放Dom操作复杂性 刻不容缓 因为:Dom渲染慢,而JS解析编译相对非常非常非常快! ...
- 深入理解React虚拟DOM
一.什么是虚拟DOM 虚拟DOM可以看做一棵模拟了DOM树的JavaScript对象树.比如: var element = { element: 'ul', props: { id:"uli ...
- React 虚拟 DOM 的差异检测机制
React 使用虚拟 DOM 将计算好之后的更新发送到真实的 DOM 树上,减少了频繁操作真实 DOM 的时间消耗,但将成本转移到了 JavaScript 中,因为要计算新旧 DOM 树的差异嘛.所以 ...
- Virtual DOM 虚拟DOM的理解(转)
作者:戴嘉华 转载请注明出处并保留原文链接( #13 )和作者信息. 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步骤一:用JS对象模拟DOM ...
- 关于react虚拟DOM的研究
1.传统的前端是这样的,我在学校也都是这样做的,html(jsp)主要负责提供所有的DOM节点,而javascript负责动态效果,比如按钮点击,图片轮播等,这样的话javascript如何组织结构是 ...
- react系列一,react虚拟dom如何转成真实的dom
react,想必作为前端开发一定不陌生,组件化以及虚拟dom使得react成为最受欢迎额前端框架之一.我们知道react是基于虚拟dom的,但是什么是虚拟dom呢,其实就是一组js对象,那么我们今天就 ...
- 浅谈React虚拟DOM
为什么要使用虚拟DOM 因为浏览器的DOM渲染是非常消耗性能的,很低效,我们使用虚拟DOM是为了提高DOM的渲染性能: 什么是虚拟DOM 虚拟DOM就是把真实的DOM树通过createElement转 ...
- React/虚拟DOM
在说虚拟DOM之前,先来一个引子,从输入url到展现出整个页面都有哪些过程? 1.输入网址 2.DNS解析 3.建立tcp连接 4.客户端发送HTPP请求 5.服务器处理请求 6.服务器响应请求 7. ...
随机推荐
- 【SHELL】跨行内容查找、替换、删除
跨行内容查找.替换.删除 sed '/START-TAG/{:a;N;/END-TAG/!ba};/ID: 222/d' data.txt /START-TAG/ { # Match 'START-T ...
- Linux-搜索-文件-find-locate-内容过滤-grep
- [转帖]Linux系统管理-crond、chkconfig、systemd、unit、target
https://cloud.tencent.com/developer/article/1409845 10.23 linux任务计划cron crontab命令被用来提交和管理用户的需要周期性执行的 ...
- [转帖]防火墙、DCD与TCP Keep alive
https://www.laoxiong.net/tag/network 在以前我写的一篇文章<Oracle与防火墙>中提到,网络防火墙会切断长时间空闲的TCP连接,这个空闲时间具体多长可 ...
- [转帖]部署Alertmanager
https://flashcat.cloud/docs/content/flashcat-monitor/prometheus/alert/manager-install/ Alertmanager和 ...
- 不同linux发行版 FIO测试结果总结
不同linux发行版 FIO测试结果总结 背景 机器来源 配置: 2路28核心Golden 6330 2.0Ghz 512G内存 硬盘 24块 960G SSD (22块 Raid5 + 2块 hot ...
- [转帖]013 Linux 搞懂「文件所属者更改及权限的赋予」从未如此简单 (chmod、chgrp、chown)
https://my.oschina.net/u/3113381/blog/5435014 01 一图详解「ls -l」 02 两种符号区分表示文件和目录 -(横线) # 表示非目录文件 d # ...
- [转帖]C2C - False Sharing Detection in Linux Perf
https://joemario.github.io/blog/2016/09/01/c2c-blog/ Do you run your application in a NUMA environme ...
- 小程序之使用阿里字体图标 定义主题的颜色 控制首页标题的样式 如何使用组件 水平居中和垂直居中的方式 H5 关于上线后,
项目搭建 1==> 需要创建的文件夹 styles 存放公共的样式 components 存放组件 lib第三方库的 utils 自己的帮助库 reques 自己的接口 2==>如何快速创 ...
- 【K哥爬虫普法】大众点评VS百度地图,论“数据权属”对爬虫开发的罪与罚!
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识,知 ...