美文网首页前端开发那些事
[H5]DOM和Virtual DOM之间的区别

[H5]DOM和Virtual DOM之间的区别

作者: 肖浩呗 | 来源:发表于2016-11-30 12:15 被阅读307次

    DOM

    • DOM代表了Document Object Model
    • DOM是一种抽象化的结构性文本
    • DOM的作用只是需要能够直接获取东西
      对于Web Developers来说,这种结构性的文本就是HTML code,并且 DOM被简单的称为HTML DOM。HTML code中的elements就变成了DOM中的nodes

    所以,既然HTML是一种文本,那么DOM就是HTML在内存中的表现形式。

    将它与程序的一个实例进行比较。你可以在同一个程序的多个过程,就像你可以有多个相同的HTML DOM(例如同一页面加载多标签)。

    HTML DOM提供了能够过去和修改Node的API,例如:getElementByIdremoveChild
    我们通常使用JavaScript来操作DOM对象。

    下面举个栗子:

    var item = document.getElementById("hei");
    item.parentNode.removeChild(item);
    

    document对象是HTML DOM中root node的一个抽象体。

    遇到的问题

    由于HTML文档的结构允许,所以HTML DOM使用的是树结构。这就很厉害了,因为通过树结构我们可以很容易地遍历树。不幸的是,很容易操作并不意味着很快
    现在的WEB程序中,DOM树�非常巨大。因为我们越来越推向动态Web应用程序(Single Page Applications - SPAs),我们需要大量的不断的修改DOM树。�这真的非常痛苦。
    顺便说一下,假如我自己设法创造一个5GB +源网页。它其实不是很难。但是考虑一下,我们将要在DOM使成千上万的div。记住,我们是现代的程序猿,我们写出的代码应该很容易管理!我们的方法,处理事件-点击提交,很多,型INS…
    比方说,典型的jQuery事件处理程序看起来像这一样:
    1、查找对事件感兴趣的每一个节点
    2、必要时更新
    其中有两个问题:

    • 很难管理。
      想象一下,你必须调整一个事件处理程序。如果你失去了背景,你必须潜水真正深入到代码,甚至知道发生了什么事。

    • 耗时和错误风险。
      它的效率很低。我们真的需要手动做所有这些调查结果吗?也许我们可以更聪明,并提前告诉哪些节点是要更新?

    那么Virtual DOM则是为了解决以上这些问题。

    Virtual DOM

    首先需要讲明的一点Virtual DOM技术并不是React发明的,但是React确实是使他发扬光大的。

    Virtual DOM是HTML DOM的抽象。它是轻量级和脱离浏览器的具体实施细节。因为DOM本身已经是一个抽象的、虚拟的DOM,事实上,Virtual DOM是一个抽象的抽象。

    实际上,Virtual DOM包含:

    • Javascript DOM模型树(VTree),类似文档节点树(DOM)
    • DOM模型树转节点树方法(VTree -> DOM)
    • 两个DOM模型树的差异算法(diff(VTree, VTree) -> PatchObject)
    • 根据差异操作节点方法(patch(DOMNode, PatchObject) -> DOMNode)

    VTree

    VTree模型非常简单,基本结构如下:

    { 
      // tag的名字 
      tagName: 'p', 
      // 节点包含属性 
      properties: { style: { color: '#fff' } }, 
      // 子节点 
      children: [], 
      // 该节点的唯一表示,后面会讲有啥用
      key: 1
    }
    

    所以我们很容易写一个方法来创建这种树状结构,例如React是这么创建的:

    // 创建一个divreact.createElement('div', null, [ 
      // 子节点img 
      react.createElement('img', { src: "avatar.png", class: "profile" }), 
      // 子节点h3 
      react.createElement('h3', null, [[user.firstName, user.lastName].join(' ')])
    ]);
    

    VTree -> DOM

    这方法也不太难,我们实现一个简单的:

    function create(vds, parent) { 
      // 首先看看是不是数组,如果不是数组统一成数组 
      !Array.isArray(vds) && (vds = [vds]); 
      // 如果没有父元素则创建个fragment来当父元素 
      parent = parent || document.createDocumentFragment(); var node; 
      // 遍历所有VNode 
      vds.forEach(function (vd) { 
          // 如果VNode是文字节点 
          if (isText(vd)) { 
            // 创建文字节点 
            node = document.createTextNode(vd.text); 
            // 否则是元素
          } else { 
            // 创建元素 
            node = document.createElement(vd.tag); 
          } 
          // 将元素塞入父容器 
          parent.appendChild(node); 
          // 看看有没有子VNode,有孩子则处理孩子
          VNode vd.children && vd.children.length && create(vd.children, node); 
          // 看看有没有属性,有则处理属性 
          vd.properties && setProps({ style: {} }, vd.properties, node); }); 
          return parent;}
    

    diff(VTree, VTree) -> PatchObject

    差异算法是Virtual DOM的核心,实际上该差异算法是个取巧算法(当然你不能指望用O(n^3)的复杂度来解决两个树的差异问题吧),不过能解决Web的大部分问题。
    那么React是如何取巧的呢?

    • 分层对比

    如图,React仅仅对同一层的节点尝试匹配,因为实际上,Web中不太可能把一个Component在不同层中移动。

    • 基于key来匹配
      还记得之前在VTree中的属性有一个叫key的东东么?这个是一个VNode的唯一识别,用于对两个不同的VTree中的VNode做匹配的。



      这也很好理解,因为我们经常会在Web遇到拥有唯一识别的Component(例如课程卡片、用户卡片等等)的不同排列问题。

    • 基于自定义元素做优化
      React提供自定义元素,所以匹配更加简单。


    patch(DOMNode, PatchObject) -> DOMNode

    由于diff操作已经找出两个VTree不同的地方,只要根据计算出来的结果,我们就可以对DOM的进行差异渲染。

    总结:

    • 给定一个可以代表DOM结构的VTree结构
    • 通过vdom/create-element函数来创建DOM中的节点
    • 通过将vdom/patch函数将vtree/diff函数根据两个不同的vtree结构产生的patch包,更新的DOM中

    引用

    前沿技术揭秘
    The difference between Virtual DOM and DOM

    扩展阅读

    Matt-Esch/virtual-dom

    相关文章

      网友评论

        本文标题:[H5]DOM和Virtual DOM之间的区别

        本文链接:https://www.haomeiwen.com/subject/jlzzpttx.html