DOM
是很慢的,为什么慢的原因以及在「浏览器渲染原理」章节中说过,这里就不再赘述了- 那么相较于 DOM
来说,操作 JS
对象会快很多,并且我们也可以通过 JS
来模拟 DOM
const ul = {
tag: 'ul',
props: {
class: 'list'
},
children: {
tag: 'li',
children: '1'
}
}
上述代码对应的 DOM
就是
<ul class='list'>
<li>1</li>
</ul>
DOM
可以通过 JS
对象来模拟,反之也可以通过 JS
对象来渲染出对应的 DOM
。当然了,通过 JS
来模拟 DOM
并且渲染对应的 DOM
只是第一步,难点在于如何判断新旧两个 JS
对象的最小差异并且实现局部更新 DOM
首先
DOM
是一个多叉树的结构,如果需要完整的对比两颗树的差异,那么需要的时间复杂度会是O(n ^ 3)
,这个复杂度肯定是不能接受的。于是React
团队优化了算法,实现了O(n)
的复杂度来对比差异。 实现O(n)
复杂度的关键就是只对比同层的节点,而不是跨层对比,这也是考虑到在实际业务中很少会去跨层的移动DOM
元素。 所以判断差异的算法就分为了两步
在第一步算法中我们需要判断新旧节点的
tagName
是否相同,如果不相同的话就代表节点被替换了。如果没有更改tagName
的话,就需要判断是否有子元素,有的话就进行第二步算法。
在第二步算法中,我们需要判断原本的列表中是否有节点被移除,在新的列表中需要判断是否有新的节点加入,还需要判断节点是否有移动。
举个例子来说,假设页面中只有一个列表,我们对列表中的元素进行了变更
// 假设这里模拟一个 ul,其中包含了 5 个 li
[1, 2, 3, 4, 5]
// 这里替换上面的 li
[1, 2, 5, 4]
从上述例子中,我们一眼就可以看出先前的
ul
中的第三个li
被移除了,四五替换了位置。
那么在实际的算法中,我们如何去识别改动的是哪个节点呢?这就引入了
key
这个属性,想必大家在Vue
或者React
的列表中都用过这个属性。这个属性是用来给每一个节点打标志的,用于判断是否是同一个节点。
DOM
,实现性能的最优化。当然了
Virtual DOM
提高性能是其中一个优势,其实最大的优势还是在于:
Virtual DOM
作为一个兼容层,让我们还能对接非 Web
端的系统,实现跨端开发。Virtual DOM
我们可以渲染到其他的平台,比如实现 SSR
、同构渲染等等。