伟大的虚拟DOM到真实DOM的过程

2 篇文章 0 订阅

虚拟DOM大家都不陌生,研究了一下略带收获,在这里简单记录下,做个备忘

1、什么是虚拟DOM以及虚拟DOM的表达

虚拟DOM简而言之就是,用JS去按照DOM结构来实现的树形结构对象也就是一个JS对象而已。那么什么样的对象才能比较匹配的上dom结构呢?来看一下如下图所示的dom结构

<div class="list" style="color: red;">
    <div class="item">
        <p class="leftP">
            <span class="left" style="margin-right: 10px;">leftSpan</span>
            <span class="right">rightSpan</span></p>
        <p class="rightP">
            <img class="imgs" src="https://pics2.baidu.com/feed/94cad1c8a786c9175fd11173392345ca3ac75749.jpeg?token=8d28956f2c93b2e7090e89980059a553&amp;s=FAC2B144C3BCB469044C8C830000B081" style="width: 100px; height: 100px;">
            <span class="imgs" style="width: 100px; height: 100px;">图片旁边的span</span>
        </p>
    </div>
    <p class="item">玉溪</p>
    <p class="item">黄鹤楼</p>
</div>

上图是比较简单的一个结构,比较理想的是使用如下对象来模拟:

{
   //节点类型
  "type": "div",
   //节点属性 
  "props": {
    "class": "list",
    "style": "color:red;"
  },
   //子节点
  "children": [
    {
      "type": "div",
      "props": {"class": "item"},
      "children": [
        {
          "type": "p",
          "props": {"class": "leftP"},
          "children": [
            {
              "type": "span",
              "props": {
                "class": "left",
                "style": "margin-right:10px"
              },
              "children": ["leftSpan"]
            },
            {
              "type": "span",
              "props": {"class": "right"},
               //没有子节点了,直接显示文本
              "children": ["rightSpan"]
            }
          ]
        },
        {
          "type": "p",
          "props": {"class": "rightP"},
          "children": [
            {
              "type": "img",
              "props": {
                "class": "imgs",
                "style": "width:100px;height:100px",
                "src": "https://pics2.baidu.com/feed/94cad1c8a786c9175fd11173392345ca3ac75749.jpeg?token=8d28956f2c93b2e7090e89980059a553&s=FAC2B144C3BCB469044C8C830000B081"
              },
              "children": []
            },
            {
              "type": "span",
              "props": {
                "class": "imgs",
                "style": "width:100px;height:100px"
              },
              "children": ["图片旁边的span"]
            }
          ]
        }
      ]
    },
    {
      "type": "p",
      "props": {"class": "item"},
      "children": ["玉溪"]
    },
    {
      "type": "p",
      "props": {"class": "item"},
      "children": ["黄鹤楼"]
    }
  ]
}

OK,对象结构已经解析完成,接下来我们就看怎么批量生成这样的结构,直接上代码

// 定义一个构造函数来
class Element {
    constructor(type, props, children) {
        this.type = type;
        this.props = props;
        this.children = children;
    }
};

 //生成虚拟dom对象 (批量调用构造函数,)
 class  createElement=(type,props,children)=>{
     return new Element(type,props,children);
  };

我们只需要调用createElement即可生成上述的dom对象结构,他接收三个参数,第一个表示dom元素的类型,第二个表示要设置的属性,第三个则表示子元素。生成上述结构调用代码如下:

let virtualDom = createElement(
            'div',
            {class:'list',style:'color:red;'},
            [
                //div
                createElement(
                    'div',
                    {class:'item'},
                    [
                        //p
                        createElement(
                            'p',
                            {class:'leftP'},
                            [
                                //span
                                createElement('span',{class:'left',style:'margin-right:10px'},['leftSpan']),
                                //span
                                createElement('span',{class:'right'},['rightSpan']),
                            ]
                        ),
                        //p
                        createElement('p',{class:'rightP'},[
                            createElement(
                                'img',
                                {
                                    class:'imgs',
                                    style:'width:100px;height:100px',
                                    src:'https://pics2.baidu.com/feed/94cad1c8a786c9175fd11173392345ca3ac75749.jpeg?token=8d28956f2c93b2e7090e89980059a553&s=FAC2B144C3BCB469044C8C830000B081'
                                },
                                [],
                            ),
                            createElement(
                                'span',
                                {
                                    class:'imgs',
                                    style:'width:100px;height:100px',
                                },
                                ['图片旁边的span'],
                            ),
                        ]),
                    ]
                ),
                createElement('p',{class:'item'},['玉溪']),
                createElement('p',{class:'item'},['黄鹤楼']),
            ]
    );
    console.log(virtualDom);

我们可以在控制台很清楚的看到打印出来的结构正是我们想要的结构

2、虚拟DOM转换成真实的DOM  

上面我们已经实现了一个虚拟DOM的表达,也就是我们可以轻而易举的通过一个对象来表达一个dom了,接下来我们看看怎么把虚拟dom转换成我们的真实dom,也就是吧我们的对象转换回我们的真实DOM,从而在页面显示。

直接上代码如下:

    // 设置属性
    const setAttr = (node, key, value)=> {
        // 需要判断key是什么
        switch (key) {
        case 'value':
            // 属性是value就要看标签类型了,input和textarea的value就需要做区别
            if (node.tagName.toLowerCase() === 'input' || node.tagName.toLowerCase() == 'textarea') {
                node.value = value;
            } else {
                node.setAttribute(key, value);
            }
            break;
        case 'style':
            node.style.cssText = value;
            break;
        default:
            node.setAttribute(key, value);
            break;
        }
    }; 

const render=(domObj)=> {
        // 根据元素类型来创建dom
        let el = document.createElement(domObj.type);
    
        // 遍历props对象,然后给创建的元素el设置属性
        for (let key in domObj.props) {
            // 设置属性的方法
            setAttr(el, key, domObj.props[key]);
        }
    
        // 遍历子节点数组
        domObj.children.forEach(child =>{
            // 需要注意的是:如果child是虚拟dom,就继续递归渲染
            if (child instanceof Element) {
                child = this.render(child);
            } else {
                // 只是普通的文本内容
                child = document.createTextNode(child);
            }
            // 把子节点添加到父节点中
            el.appendChild(child);
        });
    
        return el;
    };

直接调用我们的render函数,并把对应的虚拟DOM传入即可,接着上一步,我们已经生成了了虚拟dom对象virtualDom,因此只需要调用如下:

    // 得到dom元素
    let el = render(virtualDom); 
    console.log(el);
    //插入页面
    document.getElementById('app').appendChild(el)

到此,我们的转换也就告一段了。

3、整个过程的封装

virtualDom.js

// 定义一个构造函数来
class Element {
    constructor(type, props, children) {
        this.type = type;
        this.props = props;
        this.children = children;
    }
};


export default function VirtualDom(extendsObj={}){
    Object.assign(this,extendsObj);

     // 设置属性
    const setAttr = (node, key, value)=> {
        // 需要判断key是什么
        switch (key) {
        case 'value':
            // 属性是value就要看标签类型了,input和textarea的value就需要做区别
            if (node.tagName.toLowerCase() === 'input' || node.tagName.toLowerCase() == 'textarea') {
                node.value = value;
            } else {
                node.setAttribute(key, value);
            }
            break;
        case 'style':
            node.style.cssText = value;
            break;
        default:
            node.setAttribute(key, value);
            break;
        }
    };

    //生成虚拟dom (批量调用构造函数,)
    this.createElement=(type,props,children)=>{
        return new Element(type,props,children);
    };
    //生成真实dom
    this.render=(domObj)=> {
        // 根据元素类型来创建dom
        let el = document.createElement(domObj.type);
    
        // 遍历props对象,然后给创建的元素el设置属性
        for (let key in domObj.props) {
            // 设置属性的方法
            setAttr(el, key, domObj.props[key]);
        }
    
        // 遍历子节点数组
        domObj.children.forEach(child =>{
            // 需要注意的是:如果child是虚拟dom,就继续递归渲染
            if (child instanceof Element) {
                child = this.render(child);
            } else {
                // 只是普通的文本内容
                child = document.createTextNode(child);
            }
            // 把子节点添加到父节点中
            el.appendChild(child);
        });
    
        return el;
    };
    // 将元素插入页面
    this.renderDom = (el, target)=> {
        target.appendChild(el);
    };
} 

test.js(测试文件)

import VirtualDom from './virtualDom.js';
const testDom = ()=>{ 
    let virtualDom = new VirtualDom();
    console.log(virtualDom);
    let domObj = virtualDom.createElement(
            'div',
            {class:'list',style:'color:red;'},
            [
                //div
                virtualDom.createElement(
                    'div',
                    {class:'item'},
                    [
                        //p
                        virtualDom.createElement(
                            'p',
                            {class:'leftP'},
                            [
                                //span
                                virtualDom.createElement('span',{class:'left',style:'margin-right:10px'},['leftSpan']),
                                //span
                                virtualDom.createElement('span',{class:'right'},['rightSpan']),
                            ]
                        ),
                        //p
                        virtualDom.createElement('p',{class:'rightP'},[
                            virtualDom.createElement(
                                'img',
                                {
                                    class:'imgs',
                                    style:'width:100px;height:100px',
                                    src:'https://pics2.baidu.com/feed/94cad1c8a786c9175fd11173392345ca3ac75749.jpeg?token=8d28956f2c93b2e7090e89980059a553&s=FAC2B144C3BCB469044C8C830000B081'
                                },
                                [],
                            ),
                            virtualDom.createElement(
                                'span',
                                {
                                    class:'imgs',
                                    style:'width:100px;height:100px',
                                },
                                ['图片旁边的span'],
                            ),
                        ]),
                    ]
                ),
                virtualDom.createElement('p',{class:'item'},['玉溪']),
                virtualDom.createElement('p',{class:'item'},['黄鹤楼']),
            ]
    );
    console.log(domObj );
    // 渲染dom
    let el = virtualDom.render(domObj); // 得到dom元素
    console.log(el);
    virtualDom.renderDom(el,document.getElementById('app'));
    
};

export {
    testDom
}

 

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
React中的虚拟DOM真实DOM是用于构建用户界面的两个不同的概念。虚拟DOM是React中的一种技术,它是通过在内存中创建一个轻量级的JavaScript对象来表示DOM结构的副本。这个副本被称为虚拟DOM树。React使用虚拟DOM来表示用户界面的状态,并根据需要对其进行更新。 真实DOM则是浏览器中实际存在的DOM树,它是由HTML文档解析而来,并且直接与用户交互。当使用React创建虚拟DOM后,React会比较虚拟DOM真实DOM之间的差异,并且仅更新真实DOM中需要改变的部分,从而提高性能和效率。 虚拟DOM的主要优势在于它可以在内存中进行快速的操作和计算,而不会直接影响到真实DOM。这使得React可以通过一系列优化算法来批量更新DOM,从而减少了对浏览器的重绘和重排,提高了性能。 总结来说,React的虚拟DOM是通过在内存中创建一个轻量级的JavaScript对象来表示DOM结构的副本,而真实DOM是浏览器中实际存在的DOM树。React使用虚拟DOM来管理用户界面的状态,并将其转化为真实DOM更新到浏览器中。这种机制提高了React应用的性能和效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [[react] 什么是虚拟dom虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?](https://blog.csdn.net/echolunzi/article/details/125586796)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值