React Virtual DOM 原理

发布时间 2023-08-04 17:34:11作者: KevinSteven

1. Virtual DOM是什么?

React官网定义是:The virtual DOM(VDOM) is a programming concept where an ideal,or "virtual",representation of a UI is kept in memory and synced with the "real" DOM by a library such as ReactDOM. 

中文解释即:Virtual DOM 是一种编程理念,将UI虚拟的保存在内存中,并且通过某些库渲染成真实的DOM. Virtual DOM 将UI节点抽象成JS对象。

2.UI节点抽象 :

Learn Once,Write Anywhere: 因为提供了对HTML DOM的抽象,所以在Web开发中,通常不需要去调用DOM API。也是因为抽象,所以React也可以开发Native(React Native). 体现了React的跨平台性。

3.Virtual DOM构建UI,通过Virtual DOM 渲染页面

很简单的例子,渲染state变量text的值。可以看到React是通过render方法渲染Virtual DOM(这里不考虑优化),从而绘制出真实DOM.意味着,每次修改了state的值就会执行render方法。

示例代码:

class App extends Component{

 state ={

  text:'Virtual DOM',

}

render(){

  const {text} = this.state;

  return(

  <div>

  {text}

</div>

)

}

4.Virtual DOM 是否比 原生DOM 高效?

 4.1原生DOM更新

     DOM API 调用更新UI

4.2Virtual DOM 更新

   4.2.1每次render都会产生一份新的‘react dom’

   4.2.2Virtual DOM要对新旧‘react dom’ 进行比较,从而确定在旧‘dom’的基础上进行多少变更

   4.2.3确定最优的变革策略之后调用DOM API更新UI.

Virtual DOM 渲染成HTML,在流程上会比原生DOM操作多几个步骤。看似比操作原生DOM低效,但是实际开发中实际页面会比较复杂,如果某个场景中一直在用原生DOM进行更新,增加节点或删除节点。那么同样的代码在其他场景或是其他业务中,可能并不适用。在此情况下,需要重新编写操作DOM的代码,已满足场景和业务需求。在整体代码的维护性上,会比较繁杂。同时在实际应用场景中,多次频繁操作DOM,会引起页面重绘,页面重绘一直是影响页面性能的关键指标。React在内部已经帮我们做了关于上诉问题和页面重绘的处理,因此会比原生DOM更加高效。

5.Virtual DOM为什么会比原生DOM高效?

5.1 对UI节点抽象 

 在Virtual DOM中,对HTML节点进行抽象,用JS对象的形式表示HTML.改变了过去对HTML节点的理解,呈现在用户面前的页面就是一个复杂的递归对象。

示例代码:

const globaldom ={

 tagName:'html',

children:[{

  tagName:'head',

},{

  tagName:'body',

  children:[{

    tagName:'div',

    attributes:

  {className:'test'}

      }]  

   }]

}

6.Virtual DOM Diff 

 6.1Virtual DOM如何提高性能

     6.1.1我们将render产生的Virtual DOM简称‘Vdom’;

     6.1.2 通过调用setState方法触发Vdom更新;

     6.1.3通过对比新旧‘Vdom’,确定最优实现新‘Vdom’所需的操作;

 6.2Virtual DOM Diff的层次

     6.2.1组件级别比较

     6.2.1.1Componet Diff

          

    6.2.2元素级别比较

       

以创建,移动,删除节点为主。

创建子节点示例代码

createChild:function(child,afterNode,mountImage){

  return makeInsertMarkup(mountImage,afterNode,child._mountIndex);

},

删除子节点示例代码

removeChild: function(child,node){

  return makeRemove(child,node);

},

移动子节点示例代码

if(prevChild===nextChild){

  updates = enqueue(updates,this.moveChild(prevChild,lastPlacedNode,nextIndex,lastIndex));

  lastIndex=Match.max(prevChild._mountIndex,lastIndex);

  prevChild._mountIndex = nextIndex;

}

moveChild:function(child,afterNode,toIndex,lastIndex){

  if(child._mountIndex<lastIndex){

   return makeMove(child,afterNode,toIndex);

  }

}

7.查看React源码的2个切入点

7.1动态注入

7.2嵌套循环