vuereact 虚拟dom 区别

 感谢: 蜗牛老湿的分析

Vue1.x

没有 vdom,完全的响应式,每个数据变化,都通过响应式通知机制来新建 Watcher干活,就像独立团规模小的时候,每个战士入伍和升职,都主动通知咱老李,管理方便

项目规模变大后,过多的 Watcher,会导致性能的瓶颈

 

 

 

 

 

 

React15x

而 React15时代,没有响应式,数据变了,整个新数据和老的数据做 diff,算出差异 就知道怎么去修改 dom了,就像老李指挥室有一个模型,每次人事变更,通过对比所有人前后差异,就知道了变化, 看起来有很多计算量,但是这种 immutable的数据结构对大型项目比较友好,而且 Vdom抽象成功后,换成别的平台render成为了可能,无论是打鬼子还是打国军,都用一个 vdom模式

碰到的问题一样,如果 dom节点持续变多,每次 diff的时间超过了 16ms,就可能会造成卡顿(60fps)

 

 

Vue2.x

引入vdom,控制了颗粒度,组件层面走watcher通知, 组件内部走vdom做diff,既不会有太多watcher,也不会让vdom的规模过大,diff超过16ms,真是优秀啊 就像独立团大了以后,只有营长排长级别的变动,才会通知老李,内部的自己diff管理了

 

 

React 16 Fiber

React走了另外一条路,既然主要问题是 diff导致卡顿,于是 React走了类似 cpu调度的逻辑,把 vdom这棵树,微观变成了链表,利用浏览器的空闲时间来做 diff,如果超过了 16ms,有动画或者用户交互的任务,就把主进程控制权还给浏览器,等空闲了继续,特别像等待女神的备胎

 

 

diff的逻辑,变成了单向的链表,任何时候主线程女神有空了,我们在继续蹭上去接盘做 diff,大家研究下 requestIdleCallback就知道,从浏览器角度看 是这样的

 

 

大概代码

  1. requestIdelCallback(myNonEssentialWork);
  2. // 等待女神空闲
  3. function myNonEssentialWork (deadline){
  4. // deadline.timeRemaining()>0 主线程女神还有事件
  5. // 还有diff任务没算玩
  6. while(deadline.timeRemaining()>0&& tasks.length >0){
  7. doWorkIfNeeded();
  8. }
  9. // 女神没时间了,把女神还回去
  10. if(tasks.length >0){
  11. requestIdleCallback(myNonEssentialWork);
  12. }
  13. }

 

Vue3

这里的静态提升和事件缓存刚才说过了,就不说了,其实我也很纳闷,这些静态标记和事件缓存, React本身也可以做,为啥就不实现了,连 shouldComponentUpdate都得自己定义,为啥不把默认的组件都变成 pure或者 memo呢,唉,也许这就是人生把

React给你自由, Vue让你持久,可能也是现在国内Vue和React都如此受欢迎的原因把

Vue3通过 Proxy响应式+组件内部 vdom+静态标记,把任务颗粒度控制的足够细致,所以也不太需要 time-slice了。

 

 

diff算法补充:

dom的更新策略不同
react 会自顶向下全diff.
vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
1. 在react中,当状态发生改变时,组件树就会自顶向下的全diff, 重新render页面, 重新生成新的虚拟dom tree, 新旧dom tree进行比较, 进行patch打补丁方式,局部跟新dom. 所以react为了避免父组件跟新而引起不必要的子组件更新, 可以在shouldComponentUpdate做逻辑判断,减少没必要的render, 以及重新生成虚拟dom,做差量对比过程.
2. 在 vue中, 通过Object.defineProperty 把这些 data 属性 全部转为 getter/setter。同时watcher实例对象会在组件渲染时,将属性记录为dep, 当dep 项中的 setter被调用时,通知watch重新计算,使得关联组件更新。

Diff 算法借助元素的 Key 判断元素是新增、删除、修改,从而减少不必要的元素重渲染。

基于Diff的开发建议

1、基于tree diff:

(1)开发组件时,注意保持DOM结构的稳定;即尽可能少地动态操作DOM结构,尤其是移动操作。

(2)当节点数过大或者页面更新次数过多时,页面卡顿的现象会比较明显。这时可以通过 CSS 隐藏或显示节点,而不是真的移除或添加 DOM 节点。

2、基于component diff:

(1)注意使用 shouldComponentUpdate() 来减少组件不必要的更新。

(2)对于类似的结构应该尽量封装成组件,既减少代码量,又能减少component diff的性能消耗。

3、基于element diff:

(1)对于列表结构,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响渲染性能。

(2)循环渲染的必须加上key值,唯一标识节点


posted @ 2022-02-23 22:40  小前端学习  阅读(212)  评论(0编辑  收藏  举报