• 谈谈对虚拟DOM的理解


    一、前言

      一般谈论某个东西的时候,我们都想知道它是怎么来的,那我们现在就来说说:虚拟DOM的出现,是跟随着前端框架React的诞生而诞生的,是由facebook提出来的,主要为了兼顾开发效率与性能,后来其卓越的开发性能也赢得了越来越多的开发者的认可。继react之后,Vue2.0也在其核心上面引入了虚拟DOM的概念,那接下来我们就来谈论一下虚拟DOM的原理:

    二、虚拟DOM

      虚拟DOM:virtual dom(下面我们简称为vdom,它是vue和react的核心)

      1、什么是vdom?

        vdom:可以看作是一个使用javascript模拟DOM结构的树形结构。对于我们开发者而言呢,操作DOM结构是非常昂贵的,它改动后,整个容器中的内容中的内容都要重新渲染一遍,就相当于“推倒重来”,如果项目相对来说比较复杂的话,是非常影响性能的。vdom就可以很好地解决这个问题。

        主流的框架均支持使用 JSX 的写法, JSX 最终会被 babel 编译为JavaScript 对象,用于来表示vdom,思考下列的 JSX:

        <div>
            <span className="item">item</span>
            <input disabled={true} />
        </div>

        最终会被babel编译为如下的 JavaScript对象:

        {
            type: 'div',
            props: null,
              children: [{
                  type: 'span',
                  props: {
                    class: 'item',
                },
                children: ['item'],
              },
              {       type: 'input',       props: {      disabled: true,        },     children: [],        }],       }
        
     可以注意到以下两点:

           所有的 DOM 节点都是一个类似于这样的对象:{ type: '...', props: { ... }, children: { ... }, on: { ... } }
     

      2、为什么要使用vdom?

        我们简单的来讲vdom和原生的DOM做一个对比(简单比较一下vdom和原生DOM的重绘过程):

          原生DOM:render html string + 重新创建所有的DOM元素

          vdom:render virtual dom + diff + 必要的dom更新

           和 DOM 操作比起来,js 计算是非常便宜的。vdom render + diff 显然比原生DOM渲染 html 字符串要慢,但是,它依然是纯 js 层面的计算,比起后面的 DOM 操作来说,依然便宜了太多。依次来看,就可以看出vdom很好的提高了渲染效率。

      3、vdom的使用

      上面提到了,我们可以使用vdom来对比出需要对 DOM 进行的特定更改,并单独进行这些特定更新。让我们回到我们的示例中,并使用 DOM API 进行相同的更改。

      首先,我们需要制作vdom 的副本,其中包含我们想要进行的更改。由于我们不需要使用 DOM API,因此我们实际上只需创建一个新对象。

    const copy = {
        tagName: "ul",
        attributes: { "class": "list" },
        children: [
            {
                tagName: "li",
                attributes: { "class": "list__item" },
                textContent: "List item one"
            },
            {
                tagName: "li",
                attributes: { "class": "list__item" },
                textContent: "List item two"
            }
        ]
    };
    

    copy 对象可以是用于在当前 vdom(上文的 list 对象)和更新的 vdom 之间对比出一个差异(Diff),Diff 结果如下:

    const diffs = [
        {
            newNode: { /* new version of list item one */ },
            oldNode: { /* original version of list item one */ },
            index: /* index of element in parent's list of child nodes */
        },
        {
            newNode: { /* list item two */ },
            index: { /* */ }
        }
    ]
    

      这个 Diff 提供了有关如何更新真实 DOM 的说明。一旦确定了所有差异,我们就可以批量更新 DOM 。

      例如,我们可以循环遍历每个差异,并根据 Diff 指定的内容添加新的子元素或更新旧的子元素。

    const domElement = document.getElementsByClassName("list")[0];
    
    diffs.forEach((diff) => {
    
        const newElement = document.createElement(diff.newNode.tagName);
        /* Add attributes ... */
        
        if (diff.oldNode) {
            // If there is an old version, replace it with the new version
            domElement.replaceChild(diff.newNode, diff.index);
        } else {
            // If no old version exists, create a new node
            domElement.appendChild(diff.newNode);
        }
    })

    与框架结合

    诸如 React 和 Vue 之类的框架内部使用 Virtual DOM 来对 DOM 进行更高效的更新。例如,我们的 list 组件可以用以下方式用 React 编写。

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    const list = React.createElement("ul", { className: "list" },
        React.createElement("li", { className: "list__item" }, "List item")
    );
    
    ReactDOM.render(list, document.body);
    

    如果我们想要更新列表,我们可以重写整个列表模板,然后再次调用 ReactDOM.render() ,传入新列表。

    const newList = React.createElement("ul", { className: "list" },
        React.createElement("li", { className: "list__item" }, "List item one"),
        React.createElement("li", { className: "list__item" }, "List item two");
    );
    
    setTimeout(() => ReactDOM.render(newList, document.body), 5000);
    

    因为 React 使用 Virtual DOM,即使我们重新渲染整个模板,也只更新实际更改的部分。如果我们在发生变化时查看我们的开发人员工具,我们将看到特定元素和更改元素的特定部分。

      4、Virtual DOM的优缺点

        优点:

          1、最终表现在DOM上的修改只是变更的部分,可以保证非常高效的渲染。

          2、提升了性能(JavaScript对象比DOM对象性能高),抽象了DOM的具体实现(对DOM进行了一层抽象)

         缺点:

          首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢。

      

  • 相关阅读:
    宣布降低Windows Azure 存储和计算的价格
    物联网操作系统的概念和特点
    基于Windows8与Visual Studio11开发第一个内核驱动程序
    在 C++ 中使用 PPL 进行异步编程
    现实世界的Windows Azure:采访Figlo的全球合作伙伴支持经理Nathan Brouwer
    物联网操作系统随笔
    Windows Azure Toolkit for Windows 8 Consumer Preview的升级版发布了
    上海招聘会场所
    理解 Delphi 的类(十) 深入方法[3] 调用时参数分割
    关于类的入门例子(7): 遍历窗体的所有父类
  • 原文地址:https://www.cnblogs.com/sheep0127/p/10999843.html
Copyright © 2020-2023  润新知