一、什么是虚拟dom
虚拟DOM 其实就是一棵以 JavaScript 对象 (VNode 节点) 作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。
//真实dom
<div id="app"> hello world</div>
//虚拟dom
{ tagName:'div', attrs:{id:'app' },children:[ 'hello world']}
二、为什么要使用虚拟dom
1、减少直接操作 dom 次数,从而提高程序性能
js 层面上,DOM 的操作并不慢。DOM 操作慢是慢在浏览器渲染的过程里,改变一行数据就要全部重新渲染,在大多数情况下虚拟 DOM 比 DOM 快,是因为需要更新的 DOM 节点要比原生 DOM 操作更新的节点少,浏览器重绘的时间更短。而且虚拟 DOM 的优势不在于单次的操作,用对比的算法,它可以将多次操作合并成一次操作,在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。
真实的dom上有一堆的属性和方法,直接操作DOM的话性能会变得很慢。频繁操作虚拟DOM不存在性能问题,等数据全部更新完之后只会去更新真实dom树需要更新改变的地方
直接操作真的的DOM每操作一次就会导致一次重绘和回流。使用虚拟dom,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制,从而提高性能!
2、跨平台
它的本质就是一个 JavaScript 对象,并不依赖真实平台环境,所以使它具有了跨平台的能力。它在浏览器上可以变成 DOM,在其他平台里也可以变成相应的渲染对象。同一VNode 节点可以渲染成不同平台上的对应的内容,比如:渲染在浏览器是 dom 元素节点,渲染在 Native( iOS、Android) 变为对应的控件、可以实现 SSR(Nuxt.js/Next.js)、原生应用(Weex/React Native)、小程序(mpvue/uni-app)等 、渲染到 WebGL 中等等。
Vue3 中允许开发者基于 VNode 实现自定义渲染器(renderer),以便于针对不同平台进行渲染
------------------------------------------------------------------------------------------------------------------------------
D i f f 算 法
三、Diff 算法
vue diff算法
https://juejin.cn/post/6844903607913938951 详解vue的diff算法
渲染真实DOM的开销是很大的,比如有时候我们修改了某个数据,如果直接渲染到真实dom上会引起整个dom树的重绘和重排,
有没有可能我们只更新我们修改的那一小块dom而不要更新整个dom呢?diff算法能够帮助我们。
https://segmentfault.com/a/1190000021896771
diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有 O(n)。
diff算法的在很多场景下都有应用,例如在 vue 虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较更新时,就用到了该算法。
diff算法有两个比较显著的特点:1、比较只会在同层级进行, 不会跨层级比较。2、在diff比较的过程中,循环从两边向中间收拢
diff流程 第一步 vue 的虚拟 dom 渲染真实 dom 的过程中首先会对新老 VNode 的开始和结束位置进行标记:oldStartIdx、oldEndIdx、newStartIdx、newEndIdx。
第二步 标记好节点位置之后,就开始进入到的 while 循环处理中,这里是 diff 算法的核心流程,分情况进行了新老节点的比较并移动对应的 VNode 节点。
while 循环的退出条件是直到老节点或者新节点的开始位置大于结束位置。
第三步 当 while 循环结束后,根据新老节点的数目不同,做相应的节点添加或者删除。若新节点数目大于老节点则需要把多出来的节点创建出来加入到真实 dom 中,
反之若老节点数目大于新节点则需要把多出来的老节点从真实 dom 中删除。至此整个 diff 过程就已经全部完成了。