【问题标题】:What is Virtual DOM?什么是虚拟 DOM?
【发布时间】:2020-04-13 21:01:12
【问题描述】:

最近,我查看了 Facebook 的 React 框架。它使用了一个叫做“虚拟 DOM”的概念,我不太了解。

什么是虚拟 DOM?有什么优势?

【问题讨论】:

  • 我相信虚拟 DOM 是指不在普通 DOM 中的节点。
  • 我同意上述关于适度的观点。此外,我相信这是一个非常有效和有用的问题。 “虚拟 DOM”经常被引用,但很少被定义。
  • @Rachael:您链接到的资源不包含“Virtual DOM”一词,也没有描述它的好处。所以这并不能真正帮助回答 OP 问题。

标签: javascript reactjs


【解决方案1】:

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

您可以将虚拟 DOM 视为蓝图。它包含构建 DOM 所需的所有细节,但由于它不需要进入真实 DOM 的所有重量级部分,因此可以更轻松地创建和更改。

【讨论】:

  • 这可以用于整个 DOM,而不仅仅是它的一部分吗?
  • 它基本上是抽象上的抽象,最终它会在其对象模型树中查找引用,选择 html 中的真实节点并对其进行修补。声音很棒 virtual dom,但它没有什么花哨和过度炒作。
  • “它不需要进入真正的 DOM 的所有重量级部分”是什么意思 - 重量级部分?
  • @AjayS 操作真实 DOM 效率不高,这就是它被称为重型 API 的原因。在内存中操作对象更快、更高效,更新部分 DOM 更改也更高效、更快。
  • 所以 Virtual DOM 比更新普通 DOM 更快(不是 React/Vue,只是简单的 JS)?
【解决方案2】:

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

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

因此,只要您有任何更改,例如您想向 DOM 添加另一个 <div>,那么将创建虚拟 DOM,它实际上不会对实际 DOM 进行任何更改。现在有了这个虚拟 DOM,您将检查它与当前 DOM 之间的区别。并且只会添加不同的部分(在本例中为新的<div>),而不是重新渲染整个 DOM。

【讨论】:

    【解决方案3】:

    什么是虚拟 DOM?

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

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

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

    那么,为什么要通过所有这些来生成虚拟 DOM? 简单的答案 — 这就是快速反应的原因。它通过虚拟 DOM 差异来做到这一点。比较两棵虚拟树  -  旧树和新树  -  并仅对真实 DOM 进行必要的更改。

    Source from Intro To React #2

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 与抽象有什么关系?抽象这个词在这里无关紧要
      【解决方案5】:

      virtual DOM(VDOM) 不是一个新概念:https://github.com/Matt-Esch/virtual-dom

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

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

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

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

      【讨论】:

      • 高标还是高等级?
      【解决方案6】:

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

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

      在 ReactJS 中创建静态组件所需的最基本方法是:

      您必须从渲染方法返回代码。 您必须将每个类转换为类名,因为类是 JavaScript 中的保留字。 除了更重大的变化之外,两个 DOM 之间还有一些细微差别,包括出现在 Virtual DOM 中但不出现在 HTML DOM 中的三个属性(key、ref 和 dangerouslySetInnerHTML)。

      在使用虚拟 DOM 时要了解的重要一点是 ReactElement 和 ReactComponent 之间的区别。

      ReactElement

      • ReactElement 是 DOM 元素的轻量级、无状态、不可变的虚拟表示。
      • ReactElement - 这是 React 中的主要类型,驻留在虚拟 DOM 中。
      • 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 是有状态的组件。
      • React.createClass 被视为 ReactComponent。
      • 只要状态发生变化,组件就会重新呈现。

      每当 ReactComponent 发生状态变化时,我们希望尽可能少地改变 HTML DOM,以便将 ReactComponent 转换为 ReactElement,然后可以将其插入到虚拟 DOM 中,快速轻松地进行比较和更新。

      当 React 知道差异时 - 它被转换为在 DOM 中执行的低级 (HTML DOM) 代码。

      【讨论】:

        【解决方案7】:

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

        取自here.

        【讨论】:

          【解决方案8】:

          让我们在这件事上搞清楚。 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;
          

          此时我们已经创建了一个没有显示在 dom 上的 Div,因为它 没有附上

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

          一旦我们调用:(例如,将其添加到正文中)

              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 这样的东西”——有。这是 React 工作方式的核心特性。这个问题的公认答案很好地解释了这一点。
          • 您的回答实际上是在描述一个真实的 DOM。调用 document.createElement 后,真正的 DOM 存在,不管它是否被追加。
          【解决方案9】:

          Virtual Dom 是创建一个 Dom 副本。 Virtual dom 与 dom 相比,virtual dom 只更新 dom 中发生变化的部分。它没有渲染整个 dom,它只是在 dom 中更改了 dom 的更新部分。这非常耗时,并且通过此功能,我们的应用程序运行速度很快。

          【讨论】:

            【解决方案10】:

            所有答案都很棒。我只是想出了一个类比,它可能可以给出一个现实世界的隐喻。

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

            我们都有搬家具的经历,很累(和电脑里更新视图的概念一样)。因此,每当我们想要更改位置/添加家具(节点)时,我们只想进行非常必要的更改。

            蓝图是为了实现它而出现的。我们绘制了一个新的蓝图,并比较了与原来的差异。这让我们知道哪些部分已更改,哪些部分保持不变。然后我们对真实房间进行必要的更改(更新真实 DOM 上更改的节点)。万岁。

            (可能有人会想,为什么要依赖虚拟的,不直接对比真实的DOM?嗯,打个比方,对比真实的DOM,就是要再造一个真实的房间,和它对比你原来的那个。太贵了。)

            【讨论】:

              【解决方案11】:

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

              通俗地说,

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

              附: 1. 所以不同于普通的 js 创建了全新版本的 V-DOM 并部分更新了 R-DOM。 2. React 不会更新状态的每一个变化,而是分批发送对 R-DOM 的更新。

              【讨论】:

                【解决方案12】:

                根据 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 的信息

                【讨论】:

                  【解决方案13】:

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

                  【讨论】:

                    猜你喜欢
                    • 2018-01-21
                    • 2021-06-10
                    • 2021-03-10
                    • 2021-04-28
                    • 1970-01-01
                    • 2018-02-03
                    • 2013-04-08
                    • 2016-11-14
                    相关资源
                    最近更新 更多