dom
Document Object Model是浏览器中的一个标签元素,一个div是一个dom,一个h1也是一个dom,整个html页面就是由很多个dom组成的。
虚拟dom
虚拟dom通俗讲就是一个js对象,可以看做是一个json,比如说我们可以把<div id='div'><p>123</p><p>456</p></div>
描述成这样的json(随便写的一种表达形式):
{
"type": "div",
"children": [
{"type": "p", "innerText":"123"},
{"type": "p", "innerText":"456"}
]
}
这就是内存中一个对象,虚拟dom渲染之后就是真实的dom。
加速渲染
如果有个json数据代表了要渲染到div中的p元素的文本,例如["123","456"]
对应上文的dom,那么真实dom的渲染是这样的:
var div = document.getElementById('div')
data.forEach(txt => {
var p = document.createElement('p')
div.appendChild(p)
p.innerText = txt;
})
如果数组有1w个元素,只修改了其中一个,也是需要运行整个data的foreach来进行重新渲染1w个元素。但是虚拟dom是内存中的json对象,可以先对比下json的diff,看是那个元素真正修改了,只对这个元素所对应的dom进行更新即可,例如第5000个元素出现改动,其他没有改动。那就只需要div下面找第5k个p,对其innerText进行更新即可。减少了渲染的数量。
为什么有些框架不用
虚拟dom在react、vue等框架中被使用,但是作为后来者,像svelte等都不再使用虚拟dom。
svelte官网的解释Virtual DOM is pure overhead,大体意思呢就是react的思路呢是内存里维系一个虚拟的dom,然后数据更新的时候运行diff算法看需要更新哪些dom元素。svelte则是直接编译成了js代码,也能实现定向的更新dom,而不是全部更新的效果,这样减少了一层虚拟dom封装,就更加轻便,速度更快,主要是编译等操作比较简单。
lit也没有用虚拟dom,毕竟是原生web component的嫡系,肯定是用原生的,那对于上述大量渲染的场景,lit怎么优化的呢?官网是这么说的:
实际测试连接也确实是指定dom才进行渲染
小结
我们好像有时候过度陷入到虚拟dom的必要性上面了。
react高度封装的component在转换成浏览器能识别的dom的时候,嵌套层数是非常多的。动辄大面积渲染确实可能吃不消,我去看react的最终的dom组织形式有一种当年写EF的C#程序一样,开发者代码比较简洁,但是转换为原生语言其实进行了很多的封装。退一万步讲,如果没有那么深度封装,页面的渲染其实并不是太大的问题,毕竟一个页面的数据通常也没有那么多。更何况虚拟dom不是减少渲染的唯一解决途径,定向的修改需要修改的dom才是最终目的。