今天看啥  ›  专栏  ›  猴子爱香蕉

react/vue中dom-diff简易版实现

猴子爱香蕉  · 掘金  ·  · 2018-11-05 08:27

文章预览

阅读 52

react/vue中dom-diff简易版实现

dom-diff简易版实现

一、创建虚拟dom

利用 create-react-app快速创建一个项目模板;

删掉src下的源文件,替换成 index.js

首先我们先要用一个对象定义一个虚拟DOM的数据结构:

Element {
    type: 'ul',
    props: {
        class: 'list'
    },
    children: [
        Element{
            type: 'li',
            props: {
                class: 'item'
            },
            children: ['a']
        }
    ]
}
复制代码

开始码代码实现虚拟dom的方法实现。

'虚拟DOM结构'
浏览器上查看打印的日志信息,如下:

控制台日志

既然虚拟DOM方法已经写好,下一步就要将这个虚拟dom插入到页面中,那我们可以专门写一个渲染真实节点的方法render

先遍历最外层ultypeprops两个属性

render

控制台日志

注意:input标签的value属性 还有所有标签的style属性

好了,接下来就是继续遍历children属性,此时children会有两种情况

  1. 如果是文本 直接插入;
  2. 如果是子元素,递归遍历直到最终的结果是文本;

遍历虚拟don元素转换为真实dom结构

控制台日志

下一步我们将这个实际的DOM元素结构插入到页面中

append
控制台日志

完成第一部分。


二、实现dom-diff算法

dom-diff算法就是在两棵抽象语法树的同一位置采用先序的深度遍历算法做比较,同时用补丁的形式记录需要更新的节点位置。

type不一致直接替换当前节点以及当前节点下的子节点; 如果两个父节点一致,则从左往后遍历子节点,若子节点一致,遍历子节点下的子节点,依次递归。

补丁包的定义规则如下:

1. 属性不同(type: 'ATTRS', attrs)
2. 新的节点被删除了 (type: 'REMOVE', index: xxxx)
3. 节点类型不同/新增 (type: 'REPLACE', newNode)
4. 仅仅是文本变化(type: 'TEXT', text)
复制代码

新建一个dom-diff.js,专门处理diff算法

手动调用diff方法(react中调用diff算法是在触发setState之后)

两个虚拟dom结构如下:

虚拟dom结构

先处理type相同,属性不同的情况。

属性不同

控制台日志

发现控制台已经打印到属性变化的补丁包,最后我们把属性的小补丁包存放到最外层的大补丁包中

// 补丁包 存放两个虚拟dom的差异部分
let patchs = {}
// 放到最外层的大补丁包中
if (currentPatchs.length > 0) {
  patchs[index] = currentPatchs
}
复制代码

好了 相同类型的父节点一样,在属性比较完成之后,就需要比较children的属性是否有变化 比较children属性内部元素是否变化,利用递归去遍历

let globalIndex = 0

function diffChildren (oldChildrens, newChildrens) {
  oldChildrens.forEach((child, idx) => {
    walk(child, newChildrens[idx], ++globalIndex)
  })
}
复制代码

如果一开始type类型不相同不需要再去比较,直接用新节点替换老节点即可;

// type不一致
currentPatchs.push({
  type: TYPES.REPLACE,
  newNode: newTree
})
复制代码

兼容并处理好各种情况,比如:新节点不存在的情况,新节点增加,新节点类型改变,新节点文本改变以及新节点的属性变化等情况;

最终拿到所有与旧节点有差异的对象放入patchs这样的一个补丁对象中。

控制台日志

补丁包的key就是对应新节点有变化的数据位置。


三、 打补丁更新视图

最后一步将补丁的差异对象与现有虚拟DOM节点遍历进行一一比较与替换。

开始打补丁

补丁步骤

根据之前定义的不同补丁对象结构依次处理

补丁步骤

控制台日志

大功告成!


这只是diff算法的一个简易实现,还存在一些复杂情况处理的情况以及还有很多算法上面优化的方案,不过已经让我们大概了解了diff算法的原理。

如有笔误或者其他实现不对的地方,还望大家指出,谢谢!

具体代码可以参考github链接查看:dom-diff-demo

………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览