理解虚拟dom

什么是虚拟dom?

虚拟 DOM是由一系列的 JavaScript 对象组成的树状结构,每个对象代表着一个DOM元素,包括元素的标签名、属性、子节点等信息。虚拟 DOM中的每个节点都是一个 JavaScript 对象,它们可以轻松地被创建、更新和销毁,而不涉及到实际的DOM操作。将目标所需的 UI 通过数据结构“虚拟”地表示出来,保存在内存中,然后将真实的DOM与之保持同步

主要作用

虚拟 DOM的主要作用是在数据发生变化时,通过与上一次渲染的虚拟 DOM进行对比,找出发生变化的部分,并最小化地更新实际 DOM。这种方式可以减少实际 DOM操作的次数,从而提高页面渲染的性能和效率。 总的来说,虚拟 DOM是一种用 JavaScript 对象模拟真实 DOM结构和状态的技术,它通过在内存中操作虚拟 DOM 树来减少实际 DOM操作,从而提高页面的性能和用户体验。

使用虚拟dom的优点?怎么去提高性能的

实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种GUI

减少 JavaScript 操作真实 DOM 的带来的性能消耗

最终都是去操作真实dom的为什么使用虚拟dom最终性能会好一点

当你在一次操作时,需要更新10个DOM节点,浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程

而通过VNode,同样更新10个DOM节点,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attachDOM树上,避免大量的无谓计算

使用原生的API或者jQuery去操作DOM和基于虚拟DOM的框架(如Vue和React)去操作DOM,在性能上有一些差异:

  1. 直接操作DOM: 使用原生API或者jQuery直接操作DOM,每次操作都会直接影响真实的DOM树,这可能会引起浏览器的重排(reflow)和重绘(repaint),特别是在频繁地操作DOM时,性能开销较大。

  2. 虚拟DOM:虚拟dom是真实DOM的一个抽象表示。当状态变化时,框架会先在虚拟DOM上进行所有操作,然后通过算法比较新旧虚拟DOM的差异,并计算出最小的更新操作,最后批量更新到真实的DOM上。这种方式可以减少直接操作真实DOM的次数,从而提高性能。

  3. 批量更新: 在基于虚拟DOM的框架中,通常会采用异步批量更新的策略。这意味着当状态变化时,不会立即更新DOM,而是将更新操作放入一个队列中,稍后一次性执行,从而减少了重排和重绘的次数。内存批量操作 最终合并成一次更新操作

  4. 精确更新: 虚拟DOM框架在进行DOM更新时,通常会更加精确地只更新需要变化的部分,而不是整个组件或整个页面,这也有助于提高性能。

  5. 避免不必要的计算和渲染: 在某些情况下,组件的状态可能发生变化,但这并不影响其在DOM中的表示。使用虚拟DOM,如果检测到组件的输出没有变化,则可以跳过整个渲染和DOM更新过程。

什么时候性能更差呢?

  1. 极简的DOM操作: 对于非常简单或单次的DOM操作,使用虚拟DOM可能会引入不必要的性能开销。在这种情况下,直接使用原生DOM操作可能会更快,因为避免了创建虚拟DOM、比较差异和更新虚拟DOM这一系列步骤。

  2. 虚拟DOM树太大: 如果应用程序的组件树非常庞大,而且变化频繁,虚拟DOM的维护(创建、更新、比较)可能会成为性能瓶颈。每次状态更新时,虚拟DOM框架需要处理和比较大量的节点,这可能会导致性能下降。

  3. 频繁的大规模更新: 当应用程序需要频繁更新大部分或全部界面时,虚拟DOM的优势就不那么明显了。因为即使是虚拟DOM,大规模的更新仍然需要大量的计算来比较和更新节点。

  4. 不合理的使用模式: 如果开发者不遵循最佳实践,比如在应该使用组件局部状态的地方使用全局状态,或者不合理地频繁触发更新等,也可能导致虚拟DOM的性能下降。不恰当的使用方式会引起过多的虚拟DOM重新渲染,增加计算负担。

  5. 内存使用: 虚拟DOM需要在内存中维护一份当前DOM树的表示,对于内存受限的设备或应用,这可能成为一个问题。虽然现代设备的内存通常足够大,但在一些极端情况下,这仍然可能是一个考虑因素。

  6. 使用虚拟dom也不是所有情况都是最好 比如说基于jquery也会不好一点 

    额外开销:虚拟dom需要额外的内存存储虚拟节点,并且在更新时需要进行比较和差异计算。对于一些非常简单的操作,直接使用原生DOM或jQuery可能更快,因为避免了这些额外的开销。可能不兼容:jQuery和其他直接操作DOM的库可能会直接修改DOM元素,而React等使用虚拟DOM的框架会追踪这些元素的状态。如果同时使用这两种方法,可能会导致状态不一致,使得React无法准确地更新DOM。潜在的性能问题:如果在使用虚拟DOM的应用中大量使用jQuery进行DOM操作,可能会导致性能问题。因为每次jQuery操作后,虚拟DOM框架可能需要重新渲染整个组件树来保证界面的一致性,这会带来不必要的性能开销。

在Web应用的生命周期中,主要包含初始化(挂载)、更新(重新渲染)、卸载这几个阶段。虚拟DOM的使用在不同阶段带来的优势各有侧重:

初始化(挂载)过程可能虚拟dom会让他性能变差:构建一整个虚拟dom树在转化真实dom 可能有一些性能开销

  • 构建虚拟DOM树:在应用首次加载时,框架需要根据组件的结构和状态构建整个虚拟DOM树。这个过程涉及到创建大量的虚拟DOM节点,并且可能包括复杂的计算和逻辑判断。

  • 虚拟DOM到真实DOM的转换:构建完成虚拟DOM树后,框架需要将其转换为真实的DOM节点,并将这些节点插入到DOM树中。这个过程涉及到浏览器的DOM操作,可能会触发重排和重绘,导致性能开销。

  • 初始化渲染的性能影响:对于大型应用或复杂界面,初始化渲染过程中涉及的虚拟DOM操作和真实DOM操作可能会比较耗时,从而影响应用的启动速度和用户体验。

虽然虚拟DOM在更新过程中可以提高性能,但在初始化渲染阶段,它确实引入了一些额外的开销。为了优化这一过程,开发者可以采取一些措施,如代码分割(Code Splitting)、服务端渲染(Server-Side Rendering)或增量静态生成(Incremental Static Generation),以减少首次加载时的资源量和提高渲染效率。

更新(重新渲染)过程

当应用状态变化时,需要更新UI以反映这些变化。这个阶段是虚拟DOM最能发挥优势的地方:

  • 差异比较(Diffing):虚拟DOM框架通过比较新旧虚拟DOM树的差异,只更新实际变化的部分,而不是重新渲染整个UI。

  • 批量和异步更新:许多虚拟DOM框架会集中处理更新,通过异步或批量更新的方式减少对实际DOM的操作次数,从而提高性能。

卸载过程

当组件或整个应用不再需要时,进行清理操作。虚拟DOM在这个阶段的优势不如前两个阶段明显,因为主要工作是清除和解除绑定,避免内存泄露。不过,使用虚拟DOM框架可以简化这个过程,因为框架通常会自动处理组件的清理工作。

新旧dom的对比找出差异 这个对比是怎么对比的呢?

在React中,新旧DOM的对比  是通过虚拟DOM来实现的。React维护了一个轻量级的虚拟DOM树来表示实际的DOM结构。当组件状态或属性发生变化时,React会创建一个新的虚拟DOM树,并将其与之前的虚拟DOM树进行对比,以找出实际需要更新的部分。这个对比过程大致分为以下几个步骤:

  1. 树的对比:React首先比较两棵树的根节点。如果根节点的类型不同(比如一个是<div>,另一个是<span>),React会直接删除旧树并完整地构建新树。如果根节点类型相同,React会继续比较它们的子节点。

  2. 元素类型的对比:对于同一层级的一组子节点,React会根据元素的类型进行比较。如果元素类型(比如标签名、组件类型)改变了,React会替换整个元素及其子元素。如果元素类型相同,React会保留该元素,并继续比较其属性和子元素。

  3. 属性的对比:对于同一元素的属性,React会比较新旧属性集合的差异,并仅更新变化的属性。这包括样式、事件处理器等。

  4. 列表的对比:对于列表元素,React使用key属性来识别每个列表项的唯一性。有了key,React可以更准确地判断哪些元素是新增的、哪些被移除了、哪些只是位置变化了,从而优化列表的渲染性能。

  5. 递归对比子节点:React会递归地对比每个子节点,并应用上述规则。这个过程会一直进行,直到所有节点都被比较完毕。

通过这种方式,React能够精确地确定需要进行实际DOM更新的最小集合,从而提高渲染效率。这个过程是自动进行的,开发者通常不需要直接参与其中。

react vue diff算法 的区别

Diff算法时间复杂度是O(n)

react实现O(n)的方式:

react 将 diff 算法优化到 O(n) 的时间复杂度,基于了以下三个前提策略:

1.只对同级元素进行比较。如果出现跨层级的 dom 节点更新,则不进行复用。 深度优先遍历

2.两个不同类型的组件会产生两棵不同的树形结构,React会认为这两棵树完全不同,直接替换旧的树。类型不同,整个删除

3.key属性的使用:对于列表中的元素,React使用key属性来识别每个元素的唯一性。有了key,React可以更准确地判断哪些元素是新增的、哪些被移除了、哪些只是位置变化了。

上面的三种 diff 策略,分别对应着 tree diff、component diff 和 element diff。

Vue实现O(n)的方式

Vue在进行虚拟DOM的Diff算法时,也采用了一些策略来优化性能:

  1. patch函数:Vue使用一个名为patch的函数来比较新旧虚拟DOM节点,并应用差异到真实DOM上。这个过程是递归的,但Vue通过一些优化策略减少了不必要的比较。

  2. 双端比较算法:在比较列表中的元素时,Vue采用了一种双端比较的算法。这个算法会同时从列表的头部和尾部开始比较,通过移动头尾指针来减少比较的次数。

  3. key属性的使用:和React类似,Vue也推荐为列表中的每个元素设置一个唯一的key属性。这样可以更快地识别和定位节点的变化。

  4. 静态元素的优化:Vue会跳过对静态元素和静态子树的比较,因为它们不会发生变化。这进一步减少了比较的范围。

vue和react的diff算法都是进行同层次的比较,主要有以下两点不同:

1.vue对比节点,如果节点元素类型相同,但是类名不同,认为是不同类型的元素,会进行删除重建,但是react则会认为是同类型的节点,只会修改节点属性。

2.vue的列表比对采用的是首尾指针法(也就是双端比较),而react采用的是从左到右依次比对的方式(深度优先,单向比较),当一个集合只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到第一个,从这点上来说vue的对比方式更加高效。

在 Vue 3 中,diff是指在进行虚拟 DOM 更新时,对比新旧虚拟 DOM 树的差异,然后只对实际发生变化的部分进行更新,以尽可能地减少对真实 DOM 的操作,提高页面的性能和效率diff整体策略为:深度优先,同层比较。也就是说,比较只会在同层级进行, 不会跨层级比较;比较的过程中,循环从两边向中间收拢。高效地处理虚拟 DOM 树的更新,使得页面在数据变化时能够快速响应并更新对应的视图。

React 的 Diff 算法并不会对整个树进行比较,而是采用一种深度优先、单向的比较策略。 在比较过程中,React 会标记节点的更新状态,将差异记录在变更集合中。 最终,React 会根据变更集合对实际 DOM 进行最小化的更新,以提高性能。

总的来说,Diff 算法的核心思想是Diff就是将新老虚拟DOM的不同点找到并生成一个补丁,并根据这个补丁生成更新操作,以最小化对实际 DOM 的操作,提高页面渲染的性能和效率。

react的虚拟dom是怎么进行数据渲染的?

React的虚拟DOM是其高效数据渲染的核心机制之一。虚拟DOM是一个轻量级的JavaScript对象,它是真实DOM的一个抽象表示。React使用虚拟DOM来优化渲染流程,减少对实际DOM的操作,从而提高应用的性能。下面是React通过虚拟DOM进行数据渲染的主要步骤:

  1. 创建虚拟DOM:当一个React组件渲染时,基于其当前的状态(state)和属性(props),React会构建一个虚拟DOM树。这个树是由虚拟DOM元素(React元素)组成的,它们是JavaScript对象,反映了组件应该如何在页面上呈现。

  2. 渲染虚拟DOM:React将这个虚拟DOM树转换成真实DOM节点,并将其挂载到浏览器的DOM中。这一步是在组件第一次渲染时发生的,称为“挂载”(mounting)。

  3. 状态更新:当组件的状态(state)或属性(props)发生变化时,React将基于新的状态和属性创建一个新的虚拟DOM树。

  4. 虚拟DOM比较(Diffing):React通过比较新旧两个虚拟DOM树来确定实际变化的部分。这个过程称为“对比”(diffing),它通过一系列的算法优化,尽量减少需要更新的节点数量。

  5. 更新真实DOM:一旦React知道了哪些虚拟DOM节点发生了变化,它将这些变化映射到真实DOM上。这个过程称为“提交”(committing)。React会尽量高效地更新DOM,只改变需要变化的部分,而不是重新渲染整个组件树。

  6. 批量更新和异步渲染:React有能力将多个状态更新合并成一个批量更新,以减少对虚拟DOM树的重新构建次数和对真实DOM的操作次数。React 16引入的Fiber架构还允许异步执行渲染任务,使得大型应用的性能得到了进一步的提升。

通过以上步骤,React的虚拟DOM机制使得应用能够以高效和优化的方式进行数据渲染和界面更新。这种方法最大限度地减少了DOM操作的数量,因为真实的DOM操作是Web应用中最耗性能的部分之一。

在 Vue.js 中,一系列操作指的是将虚拟 DOM 树映射到真实 DOM 的过程,这个过程通常包括以下几个主要步骤:
1.创建 VNode 树:

首先,Vue.js 根据组件的模板或者 render 函数生成虚拟 DOM 树,每个节点都是一个 VNode 对象,描述了真实 DOM 的结构。


3.更新 VNode 树:

当 Vue 组件的状态发生变化时,或者父组件重新渲染时,Vue 会重新生成一颗新的 VNode 树。这个新的 VNode 树会与之前的 VNode 树进行比较,找出两棵树之间的差异。


5.比较差异:

Vue.js 的虚拟 DOM 算法会比较新旧两棵 VNode 树之间的差异,找出哪些地方需要更新、删除或者新增 DOM 节点。


7.生成更新补丁:

将差异比较的结果抽象成一系列的更新操作,称为补丁(patch),这些补丁描述了如何将旧的 VNode 树更新到新的 VNode 树。


9.应用更新补丁:

Vue.js 将这些补丁应用到真实的 DOM 上,通过一系列的 DOM 操作(添加、修改、删除节点)来实现新旧 VNode 树的同步。


11.渲染真实 DOM:

最终,通过以上操作,Vue.js 将虚拟 DOM 树中的更新映射到真实的 DOM 上,从而实现页面的更新和重绘。

这一系列操作在 Vue.js 内部自动完成,开发者无需手动操作。Vue.js 的虚拟 DOM 算法会尽可能地优化这些操作,以提高页面渲染的性能和效率。通过虚拟 DOM 的这种方式,Vue.js 实现了高效的页面更新,提高了开发效率,同时也保证了页面的性能。

怎么实现虚拟dom

createElement 创建 VNode 的过程,每个 VNode 有 childrenchildren 每个元素也是一个VNode,这样就形成了一个虚拟树结构,用于描述真实的DOM树结构

React Fiber

工作原理

Fiber 节点保存了 Dom 节点,Fiber 树和 Dom 树互相呼应。

通过判断当前执行帧空闲空间,来选择执行对应优先级的执行任务,在当前帧结束前再次判断是否有剩余时间和任务来支持执行,没有则执行浏览器渲染任务,并循环这一过程来最大化提升渲染效率。

利用requestAnimationFrame来确保每一帧执行的任务。

利用requestIdleCallback 来指定执行哪些低优先级的任务。如果遇到超过正常帧的任务,执行完毕后就需要立即归还控制权,并使用 requestIdleCallback 来再次申请下一次更新的时间片。requestIdleCallback 会给 callback 传递IdleDeadline 来判断任务的操作状态。

  • timeRamining():用来判断用户代理预计还剩余多少闲置时间。
  • didTimeout:用来判断当前的回调函数是否因超时而被执行。

总结

Fiber 是基于碎片化任务和和交替变更浏览器控制权,来达到更好的更新效果。其通过双缓存机制,在其 Render 阶段比较 虚拟 Dom 树和 真实 Dom,并进行节点变更标记和收集变更依赖,在 Commit 阶段更新 被标记 Fiber 节点到真实 Dom 树完成整个更新和刷新流程。

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值