虚拟dom

147 阅读4分钟
       在react和vue等技术出来之前,如果我们需要修改页面的某一个节点的内容时只能通过遍历遍历dom树的方式来找到需要修改的dom节点,然后对其进行内容上的修改来达到更新UI的目的。这种方式的缺点就是每次查询几乎都需要遍历整棵dom树极大的消耗了计算资源。 

       当react和vue中引入了虚拟dom后,极大的节省了计算资源也提高了页面的渲染速度。虚拟dom其实就是用来描述dom节点的一个js对象,通过对象的嵌套来描述完整的dom树,这样的话dom的每次修改都是在js对象的属性变化,这样一来查找js对象的属性要比遍历dom树节省资源。 

        比如我们页面需要显示的是  

<div id = 'test'> 

  <div style="color:blue">节点一</div> 

  <div class="item">节点二</div> 

  <div onclick="alert(333)">节点三</div> 

</div> 

       那么它的虚拟dom就如同下面的js对象一样,(当然真正的虚拟dom不会像我写的这样简单,对象中的属性可能比我写的这个还要多) 

{     

  "flag": "HTML",

  "tag": "div",     

  "data": { "id": "test" },     

  "children": [             

    {                  

      "flag": "HTML",                  

      "tag": "p",                 

      "data": { "style": { "color": "blue" }},                 

      "children": {                        

        "flag": "TEXT",                        

        "tag": null,                        

        "data": null,                        

        "children": "节点1",                       

        "childreFlag": "EMPTY" 

       }, 

       "childrenFlag": "SINGLE", 

       "el": null 

     }, { 

       "flag": "HTML", 

       "tag": "p", 

       "data": { "class": "item" }, 

       "children": { 

         "flag": "TEXT", 

         "tag": null, 

         "data": null, 

         "children": "节点2", 

         "childreFlag": "EMPTY" 

       }, 

       "childrenFlag": "SINGLE", 

       "el": null }, { 

       "flag": "HTML", 

       "tag": "p", 

       "data": { "@click":()=>{alert(333)} }, 

       "children": { 

         "flag": "TEXT", 

         "tag": null, 

         "data": null, 

         "children": "节点3", 

         "childreFlag": "EMPTY" 

       }, 

       "childrenFlag": "SINGLE", 

       "el": null 

     }

  ], 

   "childrenFlag": "MULTIPLE", 

   "el": null 

}

那么虚拟dom如何渲染到页面上呢? 

       vue中会通过diff算法对修改的节点进行比较修改虚拟dom对应的属性,然后使用render函数将修改后的虚拟dom转换成真是dom然后挂载到父节点上。          

      react是通过虚拟 dom 和 setState 更改 data 生成新的虚拟 dom 以及 diff 算法来计算和生成需要替换的 dom 做到局部更新的 

     如何解析虚拟dom呢(这是自己写的一个render) 

 function render(vnode,container){ 

   mount(vnode,container) 

function mount(vnode,container){ 

  let {flag} = vnode; 

  if(flag === vnodeType.HTML){ 

    mountElement(vnode,container) 

  }else if(flag === vnodeType.TEXT){ 

    mountText(vnode,container) 

 } 

function mountElement(vnode,container){ 

  let dom = document.createElement(vnode.tag); 

  vnode.el = dom; 

  let {data,children,childrenFlag} = vnode; 

  if(data){ 

    for(let key in data){ 

      patchData(dom,key,null,data[key]) 

    } 

  } 

  if(childrenFlag !== childType.EMPTY){ 

    if(childrenFlag === childType.SINGLE){ 

      mount(children,dom) 

    }else if(childrenFlag === childType.MULTIPLE){ 

      for(let i=0; i<children.length; i++){ 

        mount(children[i],dom) 

      } 

    } 

  } 

  container.appendChild(dom) 

function mountText(vnode,container){ 

  let dom = document.createTextNode(vnode.children) 

  vnode.el = dom container.appendChild(dom)

function patchData(el, key,prv,next){ 

  switch(key){ 

    case 'style': 

      for(let k in next){ 

        el.style[k] = next[k] 

      } 

    break; 

    case 'class': 

      el.className = next 

    break; 

    default: 

      if(key[0] === '@'){ 

        if(next){ 

          el.addEventListener(key.slice(1),next) 

        } 

      }else{ 

        el.setAttribute(key,next) 

      } 

    break; 

  } 

}