资讯专栏INFORMATION COLUMN

虚拟DOM

alin / 641人阅读

摘要:什么是虚拟举例说明如果网页中有一个表格,表头是姓名,年级,分数。即我们用虚拟的结构替换需要处理的结构,对虚拟的进行操作之后再进行渲染,就成为了真实的数据。当状态变更的时候用修改后的新渲染的的对象和旧的虚拟对象作对比,记录着两棵树的差异。

虚拟DOM

可以看看这个文章
如何理解虚拟DOM? - 戴嘉华的回答 - 知乎

https://www.zhihu.com/questio...

深度剖析:如何实现一个 Virtual DOM 算法 #13

是什么 什么是DOM?

DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
DOM就是将网页转化为一个对象并提供操作这个对象接口(即操作这个对象的方法),所以可以通过DOM对网页中的元素进行操作。如对某个节点增加属性,增加孩子,删除等。
DOM就是网页里你看得见的对应的某个元素。

什么是虚拟DOM?

举例说明:如果网页中有一个表格,表头是姓名,年级,分数。如果我希望点击姓名表格就按照字典序排序,点击年级,按照年级从大到小排序等等操作,那么如果直接去操作DOM的话就很难实现。例如,我们删除了一个DOM结点,或者新增了一条数据,那么重新进行排序,就会删除所有DOM然后重新渲染一遍DOM。如果数据很多的话,就会很浪费资源,影响网页的性能,可能会卡顿。

为什么会卡顿呢?是因为一个节点元素实际上包含很多属性方法,创建一个DOM就包含上百条数据,加载上绑定的事件等。性能开销很大。
我可以根据DOM结构,然后自己创建一个数据结构,自己创建的这个DOM和真实的DOM 是一一映射的。然后我们操作的时候就操作自己的数据结构,数据量很小,不管进行排序或其他处理都会很迅速。处理好之后,再根据这个数据结构把它变为真实的DOM。
即我们用虚拟的DOM结构替换需要处理的DOM结构,对虚拟的DOM 进行操作之后再进行渲染,就成为了真实的数据。

有什么用

这样的好处是如果我们需要对DOM结点进行改变,那么我们只需要查看我们自己创建的虚拟DOM,看看其中哪条数据发生了改变,然后修改虚拟DOM,并把它渲染成真实的数据即可。例如我们本来就有500条数据,然后需要添加10条,那么我们只添加10条新的虚拟DOM,然后再把这10条虚拟DOM转化为真实的DOM即可,不需要从新吧510跳全部重新渲染一遍。这样性能会提升。

所谓的虚拟DOM实际上就是我们根据真实的DOM结构,创建一个和真实DOM映射的一个数据结构,然后对数据结构进行操作,最后把这个数据结构反映到真实的DOM中。

我们可以在逻辑上把这个数据结构渲染成真实的DOM,他在数据结构上和真实DOM是差不多的

举个例子:我们可以使用一个数据结构来映射DOM(用JS对象模拟DOM树):
我们将节点用一个对象来表示,tag属性表示他的种类,children属性表示他拥有的儿子数组。那么:

这就是虚拟的DOM,体积很轻量,没有所有的属性和接口!用来操作的时候不需要耗费很高的性能。
代码如下:




  
  JS Bin




let nodesData = {
  tag: "div",
  children: [
    {
      tag: "p",
      children: [
        {
          tag: "span",
          children: [
            {
              tag: "#text",
              text: "xiedaimala.com"
            }
          ]
        }
      ]
    },
    {
      tag: "span",
        children: [
          {
            tag: "#text",
            text: "jirengu.com"
          }
        ]
    }
  ]
}

接下来我们只需要将这个虚拟的DOM渲染成真实的DOM就可以了,例如写一个函数来渲染DOM。

function createElement (data){
    
}

举例说明虚拟DOM的作用:
这时我们修改了DOM,例如我们将div中的p标签中span标签的内容由xiedaimala.com修改为baidu.com,那么我们只需要修改我们创建的数据结构中的span标签text那个属性,然后将原来内存中的nodesData与修改后的nodesData2进行比较。例如:

let nodesData2 = {
  tag: "div",
  children: [
    {
      tag: "p",
      children: [
        {
          tag: "span",
          children: [
            {
              tag: "#text",
              text: "baidu.com"//这里变了
            }
          ]
        }
      ]
    },
    {
      tag: "span",
        children: [
          {
            tag: "#text",
            text: "jirengu.com"
          }
        ]
    }
  ]
}

发现span标签的text内容改变了,那么我们在修改真实DOM的时候不需要把所有的真实DOM的很多属性和方法都检索一遍,然后重新渲染一遍,而只需要重新渲染在虚拟DOM中比较出来的修改的部分,即只需要重新渲染text部分就可以了。

以下为
深度剖析:如何实现一个 Virtual DOM 算法 #13文章中的一段解释

既然原来 DOM 树的信息都可以用 JavaScript 对象来表示,反过来,你就可以根据这个用 JavaScript 对象表示的树结构来构建一棵真正的DOM树。

之前的章节所说的,状态变更->重新渲染整个视图的方式可以稍微修改一下:用 JavaScript 对象表示 DOM 信息和结构,当状态变更的时候重新渲染这个 JavaScript 的对象结构。当然这样做其实没什么卵用,因为真正的页面其实没有改变。

但是可以用新渲染的对象树去和旧的树进行对比,记录这两棵树差异。记录下来的不同就是我们需要对页面真正的 DOM 操作,然后把它们应用在真正的 DOM 树上,页面就变更了。这样就可以做到:视图的结构确实是整个全新渲染了,但是最后操作DOM的时候确实只变更有不同的地方

如何实现

简单实现:

虚拟DOM渲染为真实DOM
/**
 * @author ruoyu
 * @description 虚拟 DOM Demo
 * @todo 暂时不考虑复杂情况
 */

class VNode {
  constructor(tag, children, text) {
    this.tag = tag
    this.text = text
    this.children = children
  }

  render() {
    if(this.tag === "#text") {
      return document.createTextNode(this.text)
    }
    let el = document.createElement(this.tag)
    this.children.forEach(vChild => {
      el.appendChild(vChild.render())
    })
    return el
  }
}

/*以上为ES6写法,改为ES5写法为:*/
/*******
function VNode() {
    this.tag = tag
    this.text = text
    this.children = children
}
VNode.prototype.render = function() {
    if(this.tag === "#text") {
      return document.createTextNode(this.text)
    }
    let el = document.createElement(this.tag)
    this.children.forEach(vChild => {
      el.appendChild(vChild.render())//递归生成子节点
    })
    return el
}  
******
这几句代码的作用是将js对象表示的虚拟DOM渲染为真实的DOM
*/

/*这个函数的作用是传入几个参数,然后返回对象*/
function v(tag, children, text) {
  if(typeof children === "string") {
    text = children
    children = []
  }
  return new VNode(tag, children, text)
}


/*  这里是js对象虚拟dom的数据结构

let nodesData = {
  tag: "div",
  children: [
    {
      tag: "p",
      children: [
        {
          tag: "span",
          children: [
            {
              tag: "#text",
              text: "xiedaimala.com"
            }
          ]
        }
      ]
    },
    {
      tag: "span",
        children: [
          {
            tag: "#text",
            text: "jirengu.com"
          }
        ]
    }
  ]
}

 */

/*使用v函数将几个参数转化为对象并返回*/
let vNodes = v("div", [
      v("p", [
        v("span", [ v("#text", "xiedaimala.com") ] )
        ]
      ),
      v("span", [
        v("#text",  "jirengu.com")
        ])
    ]
  )
/*渲染为真实的DOM*/
console.log(vNodes) /*下方有打印的结果*/
console.log(vNodes.render())


我们看一下打印的结果


DOM数据更新

以下仅为简单实现,是为了理解原理,实际上要想做到很完美的虚拟DOM,需要考虑很多

function patchElement(parent, newVNode, oldVNode, index = 0) {
  if(!oldVNode) {//如果没有,直接创建新的DOM,例如patchElement(root, vNodes1)
    parent.appendChild(newVNode.render())
  } else if(!newVNode) {//删除DOM的操作,例如patchElement(root)
    parent.removeChild(parent.childNodes[index])
  } else if(newVNode.tag !== oldVNode.tag || newVNode.text !== oldVNode.text)//替换(修改)DOM操作,例如两个VNode比较简单,然后互相比较
 {
    parent.replaceChild(newVNode.render(), parent.childNodes[index])
  }  else {//递归替换孩子DOM,递归比较
    for(let i = 0; i < newVNode.children.length || i < oldVNode.children.length; i++) {
      patchElement(parent.childNodes[index], newVNode.children[i], oldVNode.children[i], i)
    }
  }
}



let vNodes1 = v("div", [
      v("p", [
        v("span", [ v("#text", "xiedaimala.com") ] )
        ]
      ),
      v("span", [
        v("#text",  "jirengu.com")
        ])
    ]
  )

let vNodes2 = v("div", [
      v("p", [
        v("span", [ 
          v("#text", "xiedaimala.com") 
          ] )
        ]
      ),
      v("span", [
        v("#text",  "jirengu.coms"),
        v("#text",  "ruoyu")
        ])
    ]
  )
const root = document.querySelector("#root")

patchElement(root, vNodes1)//创建新的DOM,
patchElement(root)//删除DOM的操作
patchElement(root, vNodes2,vNodes1)//替换(修改)DOM操作

以上只是简单实现!有很多bug

总结

问:说说虚拟DOM:
当我们修改真正的DOM树的时候,因为DOM中元素节点有许多的属性和方法,当DOM中节点过多时往往需要消耗很大的性能。
解决方法是:使用js对象来表示DOM树的信息和结构,这个js对象可以构建一个真正的DOM树。当状态变更的时候用修改后的新渲染的的js对象和旧的虚拟DOM js对象作对比,记录着两棵树的差异。把差别反映到真实的DOM 结构上最后操作真正的DOM的时候只操作有差异的部分就可以了

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/53010.html

相关文章

  • 虚拟Dom详解 - (一)

    摘要:为此也做了一些学习简单的侃一侃虚拟到底是什么虚拟详解二什么是虚拟虚拟首次产生是框架最先提出和使用的,其卓越的性能很快得到广大开发者的认可,继之后也在其核心引入了虚拟的概念。所谓的虚拟到底是什么也就是通过语言来描述一段代码。 随着Vue和React的风声水起,伴随着诸多框架的成长,虚拟DOM渐渐成了我们经常议论和讨论的话题。什么是虚拟DOM,虚拟DOM是如何渲染的,那么Vue的虚拟Dom...

    ashe 评论0 收藏0
  • 虚拟DOM

    摘要:什么是虚拟举例说明如果网页中有一个表格,表头是姓名,年级,分数。即我们用虚拟的结构替换需要处理的结构,对虚拟的进行操作之后再进行渲染,就成为了真实的数据。当状态变更的时候用修改后的新渲染的的对象和旧的虚拟对象作对比,记录着两棵树的差异。 虚拟DOM 可以看看这个文章如何理解虚拟DOM? - 戴嘉华的回答 - 知乎 https://www.zhihu.com/questio... 深度剖...

    yanwei 评论0 收藏0
  • react虚拟dom机制与diff算法

    摘要:的一个突出特点是拥有极速地渲染性能。该功能依靠的就是研发团队弄出的虚拟机制以及其独特的算法。在的算法下,在同一位置对比前后节点只要发现不同,就会删除操作前的节点包括其子节点,替换为操作后的节点。 React的一个突出特点是拥有极速地渲染性能。该功能依靠的就是facebook研发团队弄出的虚拟dom机制以及其独特的diff算法。下面简单解释一下react虚拟dom机制和diff算法的实现...

    jzman 评论0 收藏0
  • 什么是虚拟DOM

    摘要:虚拟之上延伸了什么概念由于是虚拟,延伸了的是声明式的,我们不需要具体操作。虚拟没有解决什么问题目前自己了解的,虚拟无非是对象的表示。参考链接官网怎么写你自己的虚拟 虚拟DOM的内涵和外延? 内涵 虚拟DOM它是真实DOM的内存表示,一种编程概念,一种模式。它会和真实的DOM同步,比如通过ReactDOM这种库,这个同步的过程叫做调和(reconcilation)。 描述HTML标签,使...

    hosition 评论0 收藏0
  • 虚拟Dom详解 - (二)

    摘要:第一篇文章中主要讲解了虚拟基本实现,简单的回顾一下,虚拟是使用数据描述的一段虚拟节点树,通过函数生成其真实节点。并添加到其对应的元素容器中。在创建真实节点的同时并为其注册事件并添加一些附属属性。 第一篇文章中主要讲解了虚拟DOM基本实现,简单的回顾一下,虚拟DOM是使用json数据描述的一段虚拟Node节点树,通过render函数生成其真实DOM节点。并添加到其对应的元素容器中。在创建...

    sevi_stuo 评论0 收藏0

发表评论

0条评论

alin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<