什么是虚拟DOM?

最近,我研究了Facebook的反应框架。它使用了一个叫做“虚拟DOM”的概念,我并不真正理解这个概念。

什么是虚拟DOM?它的优点是什么?

89816 次浏览

React创建了一个自定义对象树,表示DOM的一部分。例如,它不是创建一个包含UL元素的实际DIV元素,而是创建一个React。包含React的div对象。ul对象。它可以非常快速地操作这些对象,而无需实际接触真正的DOM或通过DOM API。然后,当它呈现一个组件时,它使用这个虚拟DOM来确定它需要对真实DOM做什么,以使两棵树匹配。

您可以将虚拟DOM看作是一个蓝图。它包含了构建DOM所需的所有细节,但由于它不需要真正DOM中所有的重量级部分,因此可以更容易地创建和更改它。

让我们举个例子——虽然是一个很天真的例子:如果你家里的房间里有什么乱七八糟的东西,你需要清理它,你的第一步会是什么?你是要打扫你乱糟糟的房间,还是要打扫整个房子?答案肯定是,你只打扫需要打扫的房间。这就是虚拟DOM的作用。

普通JS遍历或呈现整个DOM,而不是只呈现需要更改的部分。

因此,无论何时你有任何变化,比如你想要向你的DOM添加另一个<div>,那么虚拟DOM将被创建,它实际上不会对实际DOM做任何改变。现在使用这个虚拟DOM,您将检查它与当前DOM之间的区别。并且只有不同的部分(在本例中是新的<div>)将被添加,而不是重新呈现整个DOM。

什么是虚拟DOM?

虚拟DOM是React组件在对页面进行任何更改之前生成的真实DOM元素的内存表示。

enter image description here

它是发生在渲染函数被调用和元素在屏幕上显示之间的一个步骤。

组件的渲染方法返回一些标记,但还不是最终的HTML。它是将成为真正元素的内存表示(这是第一步),然后输出将被转换为真正的HTML,这是在浏览器中显示的内容(这是第2步)。

那么为什么要经历所有这些来生成一个虚拟DOM呢? 简单的回答——这就是让反应更快的原因。它通过虚拟DOM差分来做到这一点。比较两个虚拟树-旧的和新的-并只对真正的DOM进行必要的更改

来源自我的博客Intro To React #2

虚拟DOM是HTML DOM的抽象,它根据状态变化选择性地呈现节点的子树。它尽可能少地进行DOM操作,以使组件保持最新状态。

A virtual DOM(VDOM)不是一个新概念:< A href="https://github.com/Matt-Esch/virtual-dom" rel="noreferrer">https://github.com/Matt-Esch/virtual-dom

VDOM在战略上更新DOM,而不需要重新绘制单个页面应用程序中的所有节点。在树结构中找到一个节点很容易,但SPA应用程序的DOM树可能非常大。在事件发生时查找和更新一个/多个节点并不省时。

VDOM通过创建实际dom的高级抽象来解决这个问题。VDOM是实际DOM的高级轻量级内存树表示。

例如,考虑在DOM中添加一个节点;在内存中保留一个VDOM副本

  1. 创建带有新状态的VDOM
  2. 将它与使用差分的旧VDOM进行比较。
  3. 只更新真实DOM中的不同节点。
  4. 将新的VDOM分配为旧的VDOM。

这是对ReactJS中经常提到的Virtual DOM的简要描述和重申。

DOM(文档对象模型)是结构化文本的抽象,这意味着它由HTML代码和css组成。这些HTML元素成为DOM中的节点。前面操作DOM的方法有一些限制。虚拟DOM是在React创建或使用之前创建的字面HTML DOM的抽象,但为了我们的目的,我们将它与React js一起使用。Virtual DOM是轻量级的,并且与浏览器中的DOM实现分离。虚拟DOM本质上是给定时间DOM的截图(或副本)。从开发人员的角度来看,DOM是生产环境,Virtual DOM是本地(开发)环境。每当React应用中的数据发生变化时,就会创建一个新的用户界面的Virtual DOM表示。

为了在ReactJS中创建静态组件,最基本的方法是:

你必须从渲染方法返回代码。 您必须将每个类转换为className,因为class是JavaScript中的保留字。 除了更大的变化之外,两个DOM之间还有一些小的差异,包括在虚拟DOM中出现而在HTML DOM中没有出现的三个属性(key, ref和dangerlysetinnerhtml)

在使用Virtual DOM时,需要理解的一件重要事情是ReactElement和ReactComponent之间的区别。

ReactElement

  • ReactElement是DOM元素的轻量级、无状态、不可变的虚拟表示。
  • ReactElement -这是React中的主要类型,位于Virtual DOM中。
  • ReactElements可以被渲染成HTML DOM

    var root = React.createElement('div'); ReactDOM。render(root, document.getElementById('example'));

  • . render(root, document.getElementById('example'))
  • JSX将HTML标记编译为ReactElements

    var root = <div/>; ReactDOM。呈现(根,. getelementbyid(例子));< /代码> < / p > < /李>

ReactComponent

  • ReactComponent——ReactComponent是有状态的组件。
  • 反应。createClass被认为是一个ReactComponent。
  • 每当状态改变时,组件都会被重新呈现。

每当ReactComponent发生状态变化时,我们希望对HTML DOM的更改尽可能少,这样ReactComponent就可以转换为ReactElement,然后ReactElement可以插入到Virtual DOM中,快速方便地进行比较和更新。

当React知道diff时,它会被转换为低级代码(HTML DOM),并在DOM中执行。

这是一个简洁的概念:不直接操作DOM(容易出错并且依赖于可变状态),而是输出一个名为Virtual DOM的值。然后,虚拟DOM是带有DOM当前状态的dif,它会生成一个DOM操作列表,使当前DOM看起来像新DOM。这些操作在批量中快速应用。

取自在这里。

让我们把这件事理清楚。 React(或任何其他库)是javascript上的“层”

没有虚拟dom这样的东西, 有一个未附加的dom .

让我用简单的javascript解释一下:

 let vDom = {};     // this is a object that will be used to hold the elements


let d = document.createElement('div');
d.innerHTML = 'hi, i am a new div';


vDom['newDiv'] = d;

在这一点上,我们已经创建了一个Div没有显示在dom,因为它

但是我们可以访问它,添加属性,值,改变等等。

一旦我们调用:(对于ex,将它添加到body)

    document.body.appendChild(vDom['newDiv'])

然后我们会看到它;

 for one how saw javascript libs come and go , i suggest to any one
to do one simple thing : master JAVAscript, not layers :)

虚拟Dom创建Dom的一个副本。虚拟dom与dom比较,虚拟dom只更新dom中发生变化的部分。它没有渲染整个dom它只是改变了dom中dom的更新部分。这是非常耗时的,从这个功能,我们的应用程序工作得很快。

所有的答案都很棒。我只是想到了一个比喻,也许可以给一个现实世界的比喻。

真正的DOM就像你的房间,节点是你房间里的家具。虚拟DOM就像我们绘制当前房间的蓝图。

我们都有搬家具的经历,这很累(与在电脑中更新视图相同)。因此,每当我们想要改变位置/添加家具(节点)时,我们只想做非常必要的改变。

蓝图来拯救实现它。我们画了一个新的蓝图,并将其与原蓝图的差异进行比较。这让我们知道哪些部分被改变了,哪些部分保持不变。然后我们对真实的房间进行必要的更改(更新真实DOM上更改的节点)。华友世纪。

(有些人可能会想,为什么我们必须依赖虚拟的DOM,而不直接比较真实的DOM呢?在类比中,比较真实的DOM意味着您必须创建另一个真实的房间,并将其与原始的房间进行比较。只是太贵了。)

React的结构单元是组件。每个组件都有一个状态。每当组件的状态发生变化时,React都会修改V-DOM树。然后,将最新版本的V-DOM与以前版本的V-DOM进行比较。经过这个计算(差分)后,当React知道哪些V-DOM对象被更改时,它只修改R-DOM中的那些对象。

通俗地说,

假设我在DOM中添加了一个div元素,React会在不改变整个R-DOM的情况下创建一个V-DOM的副本。这个新创建的V-DOM与旧的V-DOM进行了比较。它只更新实际DOM中的不同节点。现在新创建的V-DOM被视为即将到来的V-DOM的前一个版本。

< p >注:1. 因此,与普通js不同的是,V-DOM创建了全新版本,R-DOM进行了部分更新。 2. React不会更新状态中的每一个变化,而是批量发送对R-DOM的更新

根据React doc: https://reactjs.org/docs/faq-internals.html#what-is-the-virtual-dom

在React世界中,术语“虚拟DOM”通常与React元素相关,因为它们是代表用户界面的对象。

import React, { Component } from 'react'; //You need to do this inside a module to import


class App extends Component{
render(){
return (
<button>Hi</button> //This returns a virtual DOM
)
}
}


return里面的代码实际上是对函数React.createElement的调用:

//render can be rewritten like this:
render(){
return [
React.createElement(
'button',
{
key: null,
ref: null,
},
'Hi',
)
]
}

返回如下内容:

{
$$typeof: Symbol.for('react.element'),
type: "button",
key: null,
ref: null,
props: {
children: 'Hi',
}
}

这是虚拟DOM。它是一个JavaScript对象,操作成本比创建的实际DOM元素低得多

document.createElement('button');

它也是一个JavaScript对象,看起来像这样:

accessKey: ""
ariaAtomic: null
ariaAutoComplete: null
ariaBusy: null
ariaChecked: null
ariaColCount: null
ariaColIndex: null
ariaColSpan: null
ariaCurrent: null
ariaDescription: null
ariaDisabled: null
ariaExpanded: null
ariaHasPopup: null
ariaHidden: null
ariaKeyShortcuts: null
ariaLabel: null
ariaLevel: null
ariaLive: null
ariaModal: null
ariaMultiLine: null
ariaMultiSelectable: null
ariaOrientation: null
ariaPlaceholder: null
ariaPosInSet: null
ariaPressed: null
ariaReadOnly: null
ariaRelevant: null
ariaRequired: null
ariaRoleDescription: null
ariaRowCount: null
ariaRowIndex: null
ariaRowSpan: null
ariaSelected: null
ariaSetSize: null
ariaSort: null
ariaValueMax: null
ariaValueMin: null
ariaValueNow: null
ariaValueText: null
assignedSlot: null
attributeStyleMap: StylePropertyMap {size: 0}
attributes: NamedNodeMap {length: 0}
autocapitalize: ""
autofocus: false
baseURI: "http://localhost:3000/"
childElementCount: 0
childNodes: NodeList []
children: HTMLCollection []
classList: DOMTokenList [value: ""]
className: ""
clientHeight: 0
clientLeft: 0
clientTop: 0
clientWidth: 0
contentEditable: "inherit"
dataset: DOMStringMap {}
dir: ""
disabled: false
draggable: false
elementTiming: ""
enterKeyHint: ""
firstChild: null
firstElementChild: null
form: null
formAction: "http://localhost:3000/"
formEnctype: ""
formMethod: ""
formNoValidate: false
formTarget: ""
hidden: false
id: ""
innerHTML: ""
innerText: ""
inputMode: ""
isConnected: false
isContentEditable: false
labels: NodeList []
lang: ""
lastChild: null
lastElementChild: null
localName: "button"
name: ""
namespaceURI: "http://www.w3.org/1999/xhtml"
nextElementSibling: null
nextSibling: null
nodeName: "BUTTON"
nodeType: 1
nodeValue: null
nonce: ""
offsetHeight: 0
offsetLeft: 0
offsetParent: null
offsetTop: 0
offsetWidth: 0
onabort: null
onanimationend: null
onanimationiteration: null
onanimationstart: null
onauxclick: null
onbeforecopy: null
onbeforecut: null
onbeforepaste: null
onbeforexrselect: null
onblur: null
oncancel: null
oncanplay: null
oncanplaythrough: null
onchange: null
onclick: null
onclose: null
oncontextmenu: null
oncopy: null
oncuechange: null
oncut: null
ondblclick: null
ondrag: null
ondragend: null
ondragenter: null
ondragleave: null
ondragover: null
ondragstart: null
ondrop: null
ondurationchange: null
onemptied: null
onended: null
onerror: null
onfocus: null
onformdata: null
onfullscreenchange: null
onfullscreenerror: null
ongotpointercapture: null
oninput: null
oninvalid: null
onkeydown: null
onkeypress: null
onkeyup: null
onload: null
onloadeddata: null
onloadedmetadata: null
onloadstart: null
onlostpointercapture: null
onmousedown: null
onmouseenter: null
onmouseleave: null
onmousemove: null
onmouseout: null
onmouseover: null
onmouseup: null
onmousewheel: null
onpaste: null
onpause: null
onplay: null
onplaying: null
onpointercancel: null
onpointerdown: null
onpointerenter: null
onpointerleave: null
onpointermove: null
onpointerout: null
onpointerover: null
onpointerrawupdate: null
onpointerup: null
onprogress: null
onratechange: null
onreset: null
onresize: null
onscroll: null
onsearch: null
onseeked: null
onseeking: null
onselect: null
onselectionchange: null
onselectstart: null
onstalled: null
onsubmit: null
onsuspend: null
ontimeupdate: null
ontoggle: null
ontransitionend: null
onvolumechange: null
onwaiting: null
onwebkitanimationend: null
onwebkitanimationiteration: null
onwebkitanimationstart: null
onwebkitfullscreenchange: null
onwebkitfullscreenerror: null
onwebkittransitionend: null
onwheel: null
outerHTML: "<button></button>"
outerText: ""
ownerDocument: document
parentElement: null
parentNode: null
part: DOMTokenList [value: ""]
prefix: null
previousElementSibling: null
previousSibling: null
scrollHeight: 0
scrollLeft: 0
scrollTop: 0
scrollWidth: 0
shadowRoot: null
slot: ""
spellcheck: true
style: CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
tabIndex: 0
tagName: "BUTTON"
textContent: ""
title: ""
translate: true
type: "submit"
validationMessage: ""
validity: ValidityState {valueMissing: false, typeMismatch: false, patternMismatch: false, tooLong: false, tooShort: false, …}
value: ""
willValidate: true

你可以在https://indepth.dev/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react/了解更多关于虚拟DOM和React的知识

React在内存中保留了真实DOM的轻量级表示,这被称为虚拟DOM。当一个对象的状态改变时,虚拟DOM只改变真实DOM中的那个对象,而不是更新所有对象。

虚拟DOM是浏览器DOM树结构的部分或完整副本。它是由React等框架创建的,并保存在内存中,以便通过应用程序代码进行操作。

在必要时,应用程序代码使用虚拟DOM来更新浏览器DOM——实际表示用户界面的DOM。

此外,值得注意的是,Virtual DOM只是一种设计模式,任何人都可以实现,甚至可以使用纯javascript。

React在将应用状态更改推送到真正的DOM之前对这些更改进行批处理。最新的虚拟DOM差分为React提供了所有受影响的节点。然后,通过一个称为协调的高效进程将受影响的虚拟DOM节点推到真实DOM。

下图展示了React中虚拟DOM与真实DOM的差异和调和


Shows DOM Diffing and Reconciliation in React

阅读更多在这里- React虚拟DOM举例说明

——< p >优势 请注意,当你在DOM树中添加、删除或修改一个节点时,浏览器需要执行计算并调整屏幕布局和内容,以使用户界面与应用程序状态同步。在高度复杂、响应式和交互式的应用程序中,您可以看到持续的数据和事件流,DOM可能需要频繁更新

更新DOM (&重绘浏览器屏幕)在高频率是一个大问题,这就是虚拟DOM在React js来拯救。