虚拟DOM与DOMDIFF的原理(一)

本文深入解析虚拟DOM的工作原理,包括虚拟DOM的创建、渲染过程及如何通过递归将虚拟DOM转化为真实DOM。同时,阐述了虚拟DOM相较于直接操作DOM在性能上的优势,如减少不必要的DOM更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于DOMDIFF原理,请点击进入任意门

虚拟DOM

virtual dom,也就是虚拟节点。它是通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点。

创建虚拟DOM

  • 本文基于react来写,但是其实跟react也没多大关系

通过createElement方法来创建虚拟DOM,这个方法有三个属性type、props、children

  • 首先,虚拟DOM本身是一个对象,例如:

图片DOM0.png

  • 那么,我们需要构建类似上图结构的一个对象,建一个index.js文件,代码如下:
 import {createElement} from './element';
 let vertualDom = createElement('ul',{class:'list'},[
  createElement('li',{class:'item'},[1]),
  createElement('li',{class:'item'},[2]),
  createElement('li',{class:'item'},[3])
 ])
 console.log(vertualDom);
  • 现在的关键就在于,如何创建一个createElement函数,使得该函数的返回值就是图片上的值,既然上述代码引用了element.js文件,那么在该文件中写如下代码:
 class Element{
  constructor(type,props,children){
   this.type = type;
   this.props = props;
   this.children = children;
  }
 }
 function createElement(type,props,children){
  return new Element(type,props,children);
 }
 export {createElement}; 

这时,打印出来的vertualDom就是图片中所示的结构。

  • 结构有了,如何将这个对象结构,转化成真正的DOM才是关键。

  • 根据结构所示,我们需要将最外层Element转化成DOM结构,然后再将children中的每个Element转化成DOM结构,这样一层层转化,就需要用到递归。

  • 当然,我们在最开始的时候已经说了,转化成虚拟DOM转化成真实的DOM,需要用到render方法,所以,我们现在来构建这个render方法。

  • 在index.js文件中:

 import {createElement,render} from './element';
 let vertualDom = createElement('ul',{class:'list'},[
  createElement('li',{class:'item'},[1]),
  createElement('li',{class:'item'},[2]),
  createElement('li',{class:'item'},[3])
 ])
 //通过render方法,返回一个真实的DOM
 let el = render(vertualDom);//这里的el就是我们的真实DOM
 console.log(vertualDom);
 console.log(el);
  • 在element.js中,写下render的逻辑:
 class Element{
  constructor(type,props,children){
   this.type = type;
   this.props = props;
   this.children = children;
  }
 }
 //设置属性
 function setAttr(node,key,value){//node给谁设,key设的属性,value设的值
  switch(key){//设置值的规则,如果需要添加,就在这里加就可以了
   case 'value'://node是一个input或者textarea
    if(node.tagName.toUpperCase() === 'INPUT' || node.tagName.toUpperCase() === 'TEXTAREA'){
     node.value = value;
    }else{
     node.setAttribute(key,value);
    }
   break;
   case 'style':
    node.style.cssText = value;
   default:
    node.setAttribute(key,value);
   break;
  }
  //其他还有很多情况,加对应的case就可以了
 }
 function createElement(type,props,children){
  return new Element(type,props,children);
 }
 //render方法可以将虚拟DOM转化成真实的DOM
 function render(eleObj){
  //这个eleObj就是虚拟DOM,也即是一开始打印的vertualDom结构
  let el = document.createElement(eleObj.type);
  for(let key in eleObj.props){
   //设置属性的方法
   setAttr(el,key,eleObj.props[key])
  }
  eleObj.children.forEach(child =>{
   //判断child是不是一个element
   child = (child instanceof Element) ? render(child) : document.createTextNode(child);
   el.appendChild(child);
  })
  return el;
 }
 export {createElement,render,Element}; 
  • render方法已经写好了,真实DOM已经打印出来了,现在就只差一步了,将真实的DOM渲染到页面上去。渲染这步极其简单,将真实的DOM插入到目标节点即可。所以我们再element.js封装一个renderDom方法,并且在index.js导入即可。
  • element.js
 ...与前面的一样

 //将DOM渲染到页面上
 function renderDom(el,target){
  target.appendChild(el);
 }
 export {createElement,render,Element,renderDom}; 
  • index.js
 import {createElement,render,renderDom} from './element';

 let vertualDom = createElement('ul',{class:'list'},[
  createElement('li',{class:'item'},[1]),
  createElement('li',{class:'item'},[2]),
  createElement('li',{class:'item'},[3])
 ])

 //通过render方法,返回一个真实的DOM
 let el = render(vertualDom);//这里的el就是我们的真实DOM
 renderDom(el,window.root);//渲染到页面上
 console.log(vertualDom);
 console.log(el);

  • 此时,在页面上就渲染出来了.

为什么通过虚拟DOM来更新节点性能好?

比如有一个ul-li列表,那么如果直接操作DOM,是整个ul都更新掉,而通过虚拟DOM来更新的话,会在虚拟DOM里算好哪些节点是要更新的,然后直接去操作这些需要更新的DOM,对于不需要更新的是不会变的。所以通过虚拟DOM来更新节点性能好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值