React Vue都使用到Virtual DOM进行渲染页面,开源snab dom AST DOM TREE分析 ,结合浏览器对虚拟DOM进行分析,同时加入虚拟DOM解决DOM的缺陷, 用双缓存和MVC的视角分析虚拟DOM
- JS操作DOM可以影响整个渲染流水线({样式计算,布局,绘制,栅格化合成等}重排重绘合成等操作),牵一发而动全身,DOM的不当操作可能引发强制同步布局(提前获取更新后的元素属性)和布局抖动
- DOM结构十分复杂,每次DOM渲染渲染引擎都需要进行各种操作, 随着应用体积增加带来更大的性能问题,
- 虚拟DOM解决问题
- 将页面改变内容应用到虚拟DOM上,不直接应用到DOM
- 变化被应用到虚拟DOM上,VDOM不急着渲染页面,仅仅调整虚拟DOM的内部状态,这样操作虚拟DOM的代价十分小
- 虚拟DOM收集到一定数量的改变时才会应用到真实DOM里面去
- 虚拟DOM执行流程(React 举例)
- 数据(根据JSX和数据更新创建)进入虚拟DOM之后分两个路径往下走: 旧的没发生变化的数据直接进入虚拟DOM,然后更新到真实DOM里面去执行渲染流水线操作然后生成React页面,新的有变化的数据进入虚拟DOM,通过与旧的虚拟DOM树比较之后,然后更新虚拟DOM,收集到一定更新之后才会更新到真实DOM中去,执行上一条路径的操作
- React fiber机制
- 数据更新会生成新的虚拟DOM,拿到新虚拟DOM之后和旧的进行比较, 然后找到变化节点,再将变化节点应用到DOm上, 最开始的虚拟DOM比较流程在递归函数(reconciliation算法),通常执行很快,但是当虚拟DOM比较复杂的情况下,比较函数占据主线程时间较长,会带来渲染卡顿问题, 所以React团队将reconciliation算法重写为Fiber reconciler,旧算法为Stack reconciler
- 协程另外一个称呼也叫fiber,这里可以把fiber和协程关联起来,意思就是在执行算法的时候让出主线程,然后再协程上面执行,这样解决了占用主线程时间较久的问题
- 屏幕需要前后缓冲区进行替换拿到新的图像数据(显卡完成),如果每次计算完一部分图像就写入缓冲区的话,有可能在显示较为复杂的图像过程中,页面效果一部分一部分显示出来,用户会感受到页面的闪烁
- 双缓存可以将计算中间结果存到另一个缓冲区中,等待计算完成后,该缓冲区拿到完成图像之后,再将缓冲区图形数据一次性复制到后缓冲区进行前后替换,这样图像输出会稳定不少
- 可以把虚拟DOM看成DOM的一个buffer(缓冲),直接把结果应用到DOM上,减少不必要的更新带来的视觉卡顿问题
-
MVC将数据和视图分离, 在复杂项目是可以减轻项目的耦合度
-
控制器(Controller)、 视图(View)、模型(Model)
-
视图数据分离,通过控制器进行通讯,路径通常是视图发生改变,通知控制器,拿到模型层的数据,然后再发送给控制器,根据状态判断是否需要更新
-
从架构角度理解前端框架
- React来看的话,React部分可以看做MVC中的视图层,结合Redux可以构建一个MVC模型: (acton/reducer控制器、Store/state模型)
- 控制器监控DOM变化, DOM变化后控制器通知模型更新数据
- 模型数据更新好后,控制器通知视图,模型数据发生变化
- 视图接收更新数据之后,根据模型提供的数据生成虚拟DOM
- 新DOM生成好之后,与老的虚拟DOM进行比较,找出变化节点
- 比较到变化节点后,React将变化节点应用到真实DOM上,触发DOM节点更新
- DOM节点更新完之后触发渲染流水线一系列的更新变化操作用于实现页面的更新
- React来看的话,React部分可以看做MVC中的视图层,结合Redux可以构建一个MVC模型: (acton/reducer控制器、Store/state模型)
- 分析了直接操作DOM触发渲染流水线的一系列反应,操作不当可能会引起强制同步布局和布局抖动等问题,所以在操作DOM上,我们学会了交给虚拟DOM去完成相关的操作,减少对DOM的操作,通过比对批量更新虚拟DOM节点然后应用到真实DOM上面,然后再触发渲染流水线的一系列操作会提升不少的效率,在不少前端流行框架中应用广泛,然后分析这些框架应用的设计模式,分析虚拟DOM中可能应用到的双缓存和MVC模型设计思想,对于站在MVC视角中理解虚拟DOM能更加理解透彻