Shyno
Don't be shy,no problem!

对于虚拟DOM我觉得要按以下顺序去理解:

   1.原生js对DOM元素更新的机制

   2.原生js更新DOM元素有什么缺陷

   3.怎么去优化更新DOM的方式

   4.什么是虚拟DOM

   5.虚拟DOM是怎么实现DOM更新的

 

 

原生js对DOM元素的更新机制

假如我需要改多个dom元素的内容,写下如下js代码

<body>
    <div id='a' >你好</div>
    <div id = 'b'>我不好</div>
    <script>
       let a = '小明'
       let b ='小红'
       let aBox = document.getElementById('a')
       let bBox = document.getElementById('b')
       aBox.onclick=function(){
            aBox.innerHTML =a+'你好'
            debugger    //特意加上debugger,用以区分是分别渲染还是一次性渲染
            bBox.innerHTML =b+'我不好'
      }
    </script>
</body>

 结果

 很明显,js对于多个dom元素的修改,浏览器会分别多次渲染.

 

原生js更新DOM元素有什么缺陷

    现如今的网页需求有大量的异步请求,有大量的网页局部更新需求.对于原生js来说,需要多次的DOM更新,那浏览器的性能将会有很大的浪费.

 

怎么去优化更新DOM的方式

    我们想象一下,假如我先统计一下有哪些需要修改的.然后统一给浏览器,告诉它这些东西需要改,它一次性改掉是不是可以.而实际上对于一个DOM元素来说确实有类似的机制.比如

dom1.style.backgroundColor = 'red'
dom1.style.width= '50px'
dom1.style.height= '50px'

   改成

.add{
  backgound-color: red;
  width: 50px;
  heiht: 50px;
}
//JS
dom2.className = 'add'

 但是,对于多个DOM来说,原生js并没有类似的方案.所以这里需要一个中介来拦截对DOM的修改,并且统计整理好给浏览器统一一次性修改.而这个"中介"就是虚拟DOM.

 

什么是虚拟DOM

    本质:js对象.

          因为js对js对象的操作远比浏览器操作DOM元素高效.所以可以用js对象来模拟浏览器对DOM元素的修改,然后告诉浏览器最终结果是什么.假如修改10个DOM元素,其中,虚拟DOM需要改10次,浏览器只需要操作一次.多了10次js对象操作,少了9次浏览器操作.性能提升了不少.

   对象特点:

          虚拟DOM要比真实的DOM少很多属性,因为它只需要记录可能修改的部分.而DOM本身是有许多不让用户操作的属性.所以它本身也更简洁.比如

let obj = {
    type:'div',
    className:'title',
    content:'标题',
    children:{
        type:'span',
        className:'title_index',
        content:'1',
        children:null
    }
}

 除了一些必要属性,还能体现层级.

 

虚拟DOM是怎么实现DOM更新的

    1.虚拟DOM是怎么监听变化

          拿react来举例.数据变化-->虚拟DOM变化.类组件是通过setState()来监听数据变化,然后去告知虚拟DOM.所以,凡是用虚拟DOM的基本都是不建议直接操作DOM的,都是通过数据-->虚拟DOM-->DOM这一套流程.

    2.怎么去合并变化

         对于react来说.因为是数据变化传递给虚拟DOM的,和多个DOM操作是不同概念的.也就是说,我多条数据变化也有可能只是一个DOM变化,也有可能是多个.这个它不关心,它只是接收你的数据,和旧数据进行对比,变了就是变了.变了多少就是多了.反正最终都是一个对象.但是,对应react,多次的this.setState也会导致多次更新,这个跟原生js多次修改DOM是一样的.但是这个是可控的,也就是说我能用一次setState修改多个DOM那就没必要用多个setState,而原生只能一次次修改.

        另外,虚拟DOM本身对比变化,和虚拟DOM的更新是有自己独特的方法的------diff算法

        diff算法底层还是相当复杂的,而且react和vue的查找规则还不一样.不过目的是一致的,即以最小的代价更新虚拟DOM.以react的diff算法为例,它规定了一些更改规则.只有以下情况,虚拟DOM元素才会被更新

   (1)当dom的种类发生变化时,比如div变成了p标签

   (2)当dom的结构发生变化时,比如由原来的父子结构变成了兄弟结构

   (3)当dom的key发生变化时,你可以为dom元素加上key属性,当(1)和(2)不满足时,key属性发生变化也会导致重新构建dom树

       也就是说,当react监听到数据变化后会生成一个new虚拟DOM,然后和old虚拟DOM通过diff算法进行对比,找出更改过后的DOM,即diff数据.

 

    3.怎么去给浏览器传达变化

           react的diff算法已经算出了diff数据,那我怎么通知浏览器去修改相关的DOM呢?

           重点是DOM fragment.当diff数据被计算出来后,会通过DocumentFragment节点更新,它的特点是,它不属于文档树,而是作为一个"缓存区",会将所有的修改暂时的缓存起来,最后再一次性将修改后的节点渲染出来,所有只需要一次修改.

 

最后总结一下:虚拟DOM是用于模拟真实DOM的数据,一次渲染会将新的虚拟DOM和旧的虚拟DOM用diff算法进行对比,对比完再通过DocumentFragment一次性更新到实际DOM上.

       

posted on 2021-10-18 18:04  Shyno  阅读(46)  评论(0编辑  收藏  举报