# dom-diff-demo **Repository Path**: kevinleeeee/dom-diff-demo ## Basic Information - **Project Name**: dom-diff-demo - **Description**: ## 虚拟节点 虚拟节点和diff算法源码实现 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-12-15 - **Last Updated**: 2021-12-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 虚拟节点 虚拟节点和diff算法源码实现 ``` //命名 vNode -> virtual Node vnPatch -> virtual Node patch rNode -> real Node rnNode -> real Node patch ``` ![image-20211214015922592](https://gitee.com/kevinleeeee/blog-hexo-image-bed/raw/master/img//image-20211214015922592.png) 功能: 1. 构建虚拟节点 2. 转换真实DOM 3. 渲染DOM节点 4. 创建补丁包 5. 给真实DOM打补丁 ``` //补丁格式: const patches = { 0: [ { //属性更改了 type: 'ATTR', attrs: { class: 'list-wrapper' } } ], 2: [ { type: 'ATTR', attrs: { class: 'title' } } ], 3: [ { type: 'TEXT', text: '特殊项' } ] 6: [ { type: 'REMOVE', index: 6 } ], 7: [ { type: 'REPLACE', newNode: newNode } ] } ``` ***问题:如何对比新老DOM?*** 利用`domDiff`函数对比新老节点处理返回补丁包 ``` //项目结构: ├─package.json ├─webpack.config.js ├─src | ├─index.html | ├─js | | ├─domDiff.js - diff算法等函数 | | ├─doPatch.js - 打补丁等函数 | | ├─Element.js - 构造函数元素对象 | | ├─index.js - 用户入口文件/模拟两个虚拟DOM函数/执行程序 | | ├─patchTypes.js - 管理补丁名称类型 | | └virtualDom.js - 创建虚拟DOM/将虚拟DOM转为真实DOM/设置属性/渲染页面函数 ├─public | └index.html ``` **实现步骤:** 1. 用户写一个执行`createElement`函数实例化之后的对象返回的结果`vDom` 2. `render`函数把虚拟节点转换为真实DOM结构 1. 给每个真实节点的标签设置属性 2. 给子元素节点再次递归`render`渲染 3. 给子文本节点创建文本 4. 将子节点插入到父真实节点里 3. `renderDOM`函数把渲染后的真实DOM插入到根节点里实现渲染页面 4. `diffDOM`函数对比两个虚拟的DOM,内部有私有属性`index`,内部执行`vNodeWalk`函数传入新老节点和`index`,函数最后返回一个补丁`patches`对象 5. `vNodeWalk`函数 1. 定义一个数组容器`vnPacth`装载补丁 2. 当没有新节点时打入移除类型为`REMOVE`和`index` 的补丁 3. 当节点类型是字符串时打入文本类型为`TEXT`和文本内容的补丁 4. 当标签名一样时 1. `attrsWalk`函数遍历标签里属性里的新老属性 2. 定义`attrPatch`对象容器 3. 当老的属性里的新老属性值不相同时把新的属性和属性值变为`attPatch`容器对象 4. 当新的属性里的老的属性值有自身的属性时把新的属性和属性值变为`attPatch`容器对象 5. 返回`attrPatch`对象容器 6. 将`attrPatch`对象容器打入属性类型为`ATTR`和属性内容的补丁 7. `childrenWalk`深度遍历子节点 5. 当替换标签名时打入属性类型为`REPLACE`和新节点的补丁 6. 给真实DOM打补丁`doPatch`函数传入真实DOM和`patches`补丁 1. 定义`finalPatches`对象和`rnIndex = 0` 2. 将`patches`补丁赋值给`finalPatches`对象 3. 执行`rNodeWalk`函数传入真实节点 1. 每次取值`finalPatches[rnIndex++]`时`rnIndex`加1 2. 将真实节点的子节点的类数组转为数组并遍历每一个子节点 3. 递归嵌套的子节点`reNodeWalk` 4. 当有补丁时去打补丁`patchAction`函数传入真实节点和真实节点的补丁 5. 遍历每一个补丁 6. 当补丁的类型为`ATTR`时遍历拿到属性底下所有的属性和属性值 1. 如果有属性值时给真实节点设置属性 2. 有属性但没有属性值时删除真实节点下的属性 7. 当补丁的类型为`TEXT`时将真实节点的文本内容填入补丁里的`text`内容 8. 当补丁的类型为`REPLACE`时 1. 如果新的节点是虚拟节点时将它`render`渲染出来创建 2. 如果新的节点不是虚拟节点时将它作为文本节点创建 3. 将原来的节点替换为新的节点 9. 当补丁的类型为`REMOVE`时删除自己的节点 **用户使用的顺序:** 1. 拿到真实节点`const rDom = render(vDom1);` 2. 对比新老节点返回补丁包`const patches = domDiff(vDom1, vDom2);` 3. 渲染页面`renderDOM(rDom, document.getElementById('app'));` 4. 给真实DOM打补丁`doPatch(rDom, patches);` 源码地址:https://gitee.com/kevinleeeee/dom-diff-demo