最近,我查看了Facebook的React框架。它使用了一个名为“虚拟DOM”的概念,我并不是很理解。
什么是虚拟DOM?有什么好处?
答案 0 :(得分:152)
React创建一个表示DOM的一部分的自定义对象树。例如,它不是创建包含UL元素的实际DIV元素,而是创建包含React.ul对象的React.div对象。它可以非常快速地操作这些对象,而无需实际触及真实DOM或通过DOM API。然后,当它渲染一个组件时,它使用这个虚拟DOM来确定它需要用真正的DOM做什么才能让两棵树匹配。
您可以将虚拟DOM视为蓝图。它包含构建DOM所需的所有细节,但由于它不需要所有重量级部分进入真正的DOM,因此可以更轻松地创建和更改它。
答案 1 :(得分:24)
让我们举一个例子 - 虽然是一个非常幼稚的例子:如果你家里的房间里有些东西搞砸了,你需要清理它,你的第一步是什么?你会打扫你的房间搞砸了整个房子吗?答案肯定是您将只清洁需要清洁的房间。这就是虚拟DOM所做的事情。
普通的JS遍历或渲染整个DOM,而不是仅渲染需要更改的部分。
因此,每当您有任何更改时,就像您想要向DOM添加另一个<div>
一样,将创建虚拟DOM,实际上不会对实际DOM进行任何更改。现在有了这个虚拟DOM,您将检查它与当前DOM之间的区别。并且只会添加不同的部分(在这种情况下是新的<div>
),而不是重新渲染整个DOM。
答案 2 :(得分:12)
什么是虚拟DOM?
虚拟DOM是在对页面进行任何更改之前,由React组件生成的真实DOM元素的内存中表示。
这是在被调用的渲染函数和屏幕上元素的显示之间发生的一个步骤。
组件的render方法返回一些标记,但它还不是最终的HTML。它是将成为真实元素的内存中表示(这是第1步)。然后,该输出将转换为真实的HTML,这是在浏览器中显示的内容(这是第2步)。
那么为什么要通过这一切来生成虚拟DOM呢? 简单的答案 - 这是允许快速反应的原因。它通过虚拟DOM差异来实现。比较两个虚拟树 - 旧的和新的 - 并且只对真实的DOM进行必要的更改。
答案 3 :(得分:8)
@OnClick({R.id.btn_frg_one, R.id.btn_frg_two})
public void addFrgToCon(View view){
switch (view.getId()){
case R.id.btn_frg_one:
addFragment(new FOne());
break;
case R.id.btn_frg_two:
addFragment(new FTwo());
break;
}
}
public void addFragment(Fragment fragment){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
(VDOM)不是新概念:https://github.com/Matt-Esch/virtual-dom 。
VDOM战略性地更新DOM而不重绘单个页面应用程序中的所有节点。在发辫结构中查找节点很容易,但SPA应用程序的DOM树可能非常庞大。在事件发生时查找和更新节点/节点不是时间有效的。
VDOM通过创建实际dom的高标签抽象来解决此问题。 VDOM是实际DOM的高级轻量级内存树表示。
例如,考虑在DOM中添加节点;反应将VDOM的副本保存在内存中
答案 4 :(得分:2)
Virtual DOM是HTML DOM的抽象,它根据状态变化有选择地呈现节点的子树。它可以实现最少量的DOM操作,以使您的组件保持最新。
答案 5 :(得分:2)
这是对React JS经常提到的Virtual DOM的简短描述和重申。
DOM(文档对象模型)是结构化文本的抽象,这意味着它由HTML代码和CSS组成。这些HTML元素成为DOM中的节点。以前的操作DOM的方法存在局限性。虚拟DOM是在创建或使用React之前就创建的文字HTML DOM的抽象,但是出于我们的目的,我们将其与ReactJS一起使用。虚拟DOM轻巧,并且与浏览器中的DOM实现分离。虚拟DOM本质上是给定时间的DOM的屏幕快照(或副本)。从开发人员的角度来看,DOM是生产环境,而Virtual DOM是本地(开发)环境。每当React应用程序中的数据发生变化时,都会创建一个新的用户界面虚拟DOM表示形式。
在ReactJS中创建静态组件所需的最基本方法是:
您必须从render方法返回代码。 您必须将每个类都转换为className,因为class是JavaScript中的保留字。 除了更主要的变化之外,两个DOM之间还存在细微差别,包括出现在虚拟DOM中但不出现在HTML DOM中的三个属性(键,ref和dragonallySetInnerHTML)。
使用虚拟DOM时要了解的重要一点是ReactElement和ReactComponent之间的区别。
ReactElement
ReactElements可以呈现为HTML DOM
var root = React.createElement('div');
ReactDOM.render(root, document.getElementById('example'));
JSX将HTML标记编译为ReactElements
var root = <div/>;
ReactDOM.render(root, document.getElementById('example'));
ReactComponent
每当ReactComponent发生状态更改时,我们都希望对HTML DOM进行尽可能少的更改,以便将ReactComponent转换为ReactElement,然后可以将其插入到Virtual DOM中,进行比较和轻松快速地进行更新和>
当React知道diff时-它会转换为在DOM中执行的低级(HTML DOM)代码。
答案 6 :(得分:1)
这是一个简洁的概念:与其直接操作DOM(它容易出错,而且依赖于可变状态),而不是直接操作DOM,而是输出一个称为Virtual DOM的值。然后,将虚拟DOM 与DOM的当前状态进行 diffed ,这会生成一系列DOM操作,这些操作会使当前DOM看起来像新的DOM。 这些操作可以快速批量应用。
取自here.
答案 7 :(得分:0)
虚拟Dom 被创建为Dom的一个副本。将虚拟dom 与dom进行比较,虚拟dom仅更新dom中已更改的部分。它并没有渲染整个dom,而只是改变了dom中dom的更新部分。这非常耗时,而且通过此功能,我们的应用可以快速运行。
答案 8 :(得分:0)
所有答案都很好。我只是想出了一个比喻,它可以给出一个真实世界的比喻。
真正的DOM就像您的房间,节点就是房间中的家具。虚拟DOM就像我们绘制当前房间的蓝图。
我们都有移动家具的经验,这非常累人(与更新计算机视图相同的概念)。因此,每当我们要更改位置/添加家具(节点)时,我们只想做非常必要的更改。
蓝图是为了实现这一目标而进行的救援。我们绘制了一个新的蓝图,并将其与原始蓝图进行比较。这使我们知道哪些部分已更改,哪些部分保持不变。然后,我们对实际房间进行必要的更改(更新真实DOM上已更改的节点)。欢呼。
(有些人可能会认为,为什么我们必须依靠虚拟的而不直接比较真实的DOM?嗯,以此类推,比较真实的DOM意味着您必须创建另一个真实的房间并将其与你原来的那个。太贵了。)
答案 9 :(得分:0)
反应的结构单元是组成部分。每个组件都有一个状态。每当组件状态改变时,React都会修改V-DOM树。此后,将V-DOM的最新版本与V-DOM的先前版本进行比较。经过计算(比较)后,当React知道哪些V-DOM对象已更改时,它仅修改R-DOM中的那些对象。
用外行的话来说,
说我在DOM中添加了一个div元素,React创建了V-DOM的副本,而不更改整个R-DOM。将此新创建的V-DOM与旧的V-DOM进行比较。它仅更新实际DOM中的不同节点。现在,新创建的V-DOM被视为即将推出的V-DOM的先前版本。
P.S。 1.因此,与普通js不同,创建了V-DOM的全新版本,并部分更新了R-DOM。 2. React不会更新状态的每一个变化,而是对R-DOM的更新分批发送。
答案 10 :(得分:0)
根据React文档: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的更多信息。答案 11 :(得分:-1)
在这件事上让我们有条理。 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上,因为它 尚未附上
但是我们可以访问它,添加属性,值,更改等。
一旦我们调用:(例如,将其添加到正文中)
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 :)
答案 12 :(得分:-1)
React 在内存中保留了真实 DOM 的轻量级表示,这被称为虚拟 DOM。当一个对象的状态发生变化时,虚拟 DOM 只会改变真实 DOM 中的那个对象,而不是更新所有的对象。