一、虚拟dom和真实dom的区别
我们在render函数里写的jsx其实就是react.creactElement的语法糖,在插件babel-plugin-transform-react-jsx的作用下,jsx就会被转为
const title = React.createElement(
'h1',
{ className: 'header' },
'Hello, world!'
)
的结构,然后在React.createElement方法中,通过确认标签类型,虚拟dom的各节点依次调用document.createTextNode()、document.createElement()方法创建真实dom,并把className、事件绑定到真实dom上,最后,把组件渲染到网页中。因此,react在初次渲染时是需要花费比原生的真实dom,jQuery等多很多的时间。那么虚拟dom的优势在哪呢?
我们都知道,节点的每次增减、移动就会导致浏览器重排,重排会消耗大量的浏览器性能,盲目操作真实dom、jQuery时可能就会导致浏览器多次重排,比如下面这段代码:
var div = document.createElement('div')
div.style.width = "200px"
div.style.position = 'absolute'
div.style.top = '100px'
上面这段代码其实让浏览器重排了四次,如果是用虚拟dom,不论一次性增加多少个节点,浏览器只会在虚拟转为真实dom后重排一次。
二、react的diff算法--如何让react做到局部改变的
react之前版本的diff算法复杂度是o(n³),现版本的复杂度是o(n)。是如何做到将复杂度优化的呢?react的diff算法是将之前的虚拟dom存储下来,和新的虚拟dom从最顶层开始,逐层比较。这里面每一层有一下几种情况,我们以根节点不变,比对到了第2层为例,共分为以下几种情况:
1. 新增一个节点,这里diff算法就是新增一个节点到第二层,没什么特殊的。
2. 将一个节点类型改变,比如从div变成了img标签,这里react的处理是会删掉div节点以及其子节点,创建一个img的节点(C > G)。
3. 将一个节点从第二层变成第三层,节点里的子元素不变。这里为了降低复杂度,则是把第二层的节点删掉,在第三层重新创建一个节点(D)。
4. 节点在同一层顺序变了。如果节点有不可变的唯一标识(key),则只是改变顺序,如果不存在,则仍进行顺序比对,发现顺序 不同则把节点删掉重新创建(A和B)。