React Virtual DOM 原理
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嵌套循环