virtualdom diff算法实现分析(三)

原创 Lin_Grady 教程 框架/库 55阅读 21 天前 举报

React系列

React简单模拟语法(一)
Jsx, 合成事件与Refs(二)
virtualdom diff算法实现分析(三)

完整代码可查看virtualdom-diff

渲染DOM

经历过PHP模板开发或者JQuery的洗礼的人都知道,它们实现重新渲染采用最简单粗暴的办法就是重新构建DOM替换旧DOM,问题也很明显

  • 性能消耗高
  • 无法保存状态(聚焦,滚动等)

我们先看看创建一个元素所包含的实例属性有多少个

然后浏览器根据CSS规则查找匹配节点,计算合并样式布局,为了避免重新计算一般浏览器会保存这些数据.但这是整个过程下来依然会耗费大量的内存和 CPU 资源.

Virtual DOM

实际也是操作Dom树进行渲染更新,但是它只是针对修改部分进行局部渲染,将影响降到最低,虽然实现方式各有不同,但是大体步骤如下:

  1. 用Javascript对象结构描述Dom树结构,然后用它来构建真正的Dom树插入文档
  2. 当状态发生改变之后,重新构造新的Javascript对象结构和旧的作对比得出差异
  3. 针对差异之处进行重新构建更新视图

无非就是利用Js做一层映射比较,操作简单并且速度远远高于直接比较Dom树

基础工具函数

无非就是一些类型判断,循环遍历的简化函数

相关代码可以查看util.js

Javascript对象结构描述

我之前讲JSX的时候举过这么个例子,然后我们就以这个来实现效果吧

创建一个Element类负责将Javascript对象结构转换为Dom树结构

新建示例,调用方式如下

运行之后能正常得出结果了,那么第一步骤算是完成了,具体还有更多不同类型标签,对应事件状态先略过.

界面如图

Javascript结构如图

结构原型如下

相关代码可以查看element.js

diff算法

这是整个实现里面最关键的一步,因为这决定了计算的速度和操作Dom的数量

我们创建新的Dom树作对比

Javascript结构如图

tree diff

传统 diff 算法的复杂度为 O(n^3),但是一般Dom跨层级的情况是非常少见的。所以React 只针对同层级Dom节点做比较,将 O(n^3) 复杂度的问题转换成 O(n) 复杂度的问题。

比较大的问题就是当节点跨层级移动并不会进行移动而是直接替换整个节点,所以切记这点性能问题

component diff

  • 某个组件发生变化,会导致自其从上往下整体替换
  • 同一类型组件会进行Virtual DOM进行比较
  • React提供了一个shouldComponentUpdate决定是否更新

尽可能将动态组件往底层节点迁移,有利于提高性能

element diff

元素操作无非就是几种,我们定义几个类型做状态标记

其中NOKEY就是专门给那些没有定义key的组件做默认,React对同一层级的同组子节点,添加唯一 key 进行区分进行位移而不是直接替换,这点对于整体性能尤为关键

我们首先针对不同类型做些区分处理

新旧节点的props属性比较

新旧节点的子元素对比

深度遍历的原型图如下

其中的listDiff来自于list-diff,能通过关键属性获得最小移动量,moves就是给第三步更新视图做铺垫指示,官方介绍如下

Diff two lists in time O(n). I The algorithm finding the minimal amount of moves is Levenshtein distance which is O(n*m). This algorithm is not the best but is enougth for front-end DOM list manipulation.

This project is mostly influenced by virtual-dom algorithm.

调用对比方式

得出差异如下

相关代码可以查看diff.js

更新视图

进行深度遍历

针对不同标志做对应处理

先说简单的属性替换

最后就是列表渲染

当这一步完成以后我们可以直接应用查看效果

结果如图

相关代码可以查看patch.js

参考

深度剖析:如何实现一个 Virtual DOM 算法

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复