React虚拟dom及dom Diff的理解

随着近年来前端行业迅速的崛起 前端已经不仅仅局限于将页面展示出来了,更多的是考虑性能的优化和用户的体验,因而越来越多的框架应运而生,angular,react ,vue 三大开源框架 目前在前端行业三足鼎立,人家站稳脚跟的原因必然是将前端的历史遗留问题解决的彻彻底底,同时在很大程度上还解决了很多其他的问题,当然最主要的还是 页面加载速度 及更新速度上。下面简单梳理下虚拟dom 和 diff算法

vue

  • vue 是mvvm模式 数据驱动视图 ,本质上是利用观察者模式(订阅者发布者)劫持数据,利用es6 的Object.definePropety()中的访问器属性 将数据进行劫持 实现数据双向绑定 。
  • vue2.0 加入了dom diff 的算法 给vue的性能锦上添花。趋向于react
  • vue 的dom刷新频率 做成与浏览器同步状态(60次/s) ,能最大的减少dom操作次数,来达到速度上的优化.
  • vue中diff 的特点是对dom 进行原地复用,利用key值精准查找虚拟dom 中的不同处 ,来达到高效更新的效果 。

React

今天的重点不在vue 而是react 仿写了一个简易的react虚拟dom 和diff 算法的简易版 大致下梳理下 diff 算法原理
react 框架最开始考虑的就是 性能问题,facebook 团队在react从0开始研发时就在设计这个问题。他是利用虚拟dom+diff算法 实现 dom的高效更新 其中最关键的就设计到一下几点

  • 虚拟dom 的实现
  • dom diff 算法的计算出需要更改的补丁对象
  • 根据补丁对象更新试图
1.虚拟dom 的实现

虚拟dom 说白了 就是真实dom树的一个描述,用js的对象去表示真实dom 我们可以创建这样一个方法 用来专门生成虚拟dom
React虚拟dom及dom Diff的理解
createElement 的具体实现
React虚拟dom及dom Diff的理解
React虚拟dom及dom Diff的理解
我们通过传入的元素名,元素属性和子节点 函数内部经过 处理 返回一个类对象用于描述 该节点 生成所谓的虚拟dom

2.映射到真实dom 树上

通过render 函数 将虚拟dom转成真实dom
React虚拟dom及dom Diff的理解
render 函数传入 虚拟dom对象 根据documnet。createElement 创建父节点,遍历虚拟dom 节点上每一个props属性和子节点给父节点添加属性描述 (考虑的情况多种多样 需要分情况考虑属性的类型) 这里专门定义一个函数 进行属性的实现
React虚拟dom及dom Diff的理解
真实dom实现完成后 就需要 追加到试图上 可自定义一个renderDOM 的方法实现 页面的渲染

DOM Diff 的实现

虚拟dom 实现完成后 我们要考虑的就是 进行两个类似的虚拟dom的对比,也就是两个对象的对比 而diff 的作用:根据两个虚拟对象创建出 补丁:(描述改变的内容),将这个补丁用来更新dom
dom diff 差异计算:
遵循先序深度优先遍历
代码逻辑:

  1. 用js模拟dom
  2. 把虚拟dom转换为真实dom 并插入页面
  3. 若有事件发生需改虚拟dom 比较两个虚拟dom差异,得到差异对象
  4. 把差异对象 应用到真实dom树上

domdiff 三种优化策略

  • 更新时只比较平级
  • 绝不会跨级比较
  • 若平级有变动只最小程度去修改dom
    React虚拟dom及dom Diff的理解
    首先有一个diff函数 传入新树和老树 比较两者区别
    然后需要个递归树 递归比较将两者不同处 放入补丁包中、

需要考虑几种情况 也就是补丁规则:

  1. 当节点类型相同时 ,
  • 查看属性 是否相同 属性不同 diff 比较后会产省一个补丁包
    {type : “ATTR”, attrs:{class:list-gourp}}‘
  • 新的dom节点不存在 时
    {type:‘REMOVE’,index:xxx}
  1. 节点类型不相同时,直接采用替换模式
    {type:“REPLACE”,newNode:newNode}
  2. 文本变化时
    {type:“TEXT”,text:1}

我们定义一个walk方法 递归比较 找出补丁包
React虚拟dom及dom Diff的理解
然后根据补丁包 给具体的元素打补丁 实现试图更新
React虚拟dom及dom Diff的理解
其实 diff 算法的本质就是 找出两个dom 对象中的不同的地方,生成补丁包,根据补丁包给元素一层层递归打补丁 实现最终的视图更新

当然react 内部源码内部实现的diff 功能及情况的考虑要更全面些 大家有时间可以研究下源码
demo源码 请狂戳 https://github.com/guanguangithub/domDiff-demo