# 虚拟 DOM 和 Diff 算法
# 虚拟 DOM
虚拟 DOM 就是用来将真实 dom 抽象为 Js 对象的形式
工作原理就是当页面内容发生改变时回生成一个新的 DOM 树与旧的 DOM 树进行比较
找出差异后以最小的更新去进行增删改
优势:
- 性能提升:会尽量减少对 DOM 的操作,从而减少操作 DOM 的次数,从而提高性能
- 跨平台:因为是抽象的对象所以可以在任何支持 Javascript 的平台上运行
# Diff 算法
Vue 的 diff 算法就离不开 Snabbdom;
Snabbdom 是一个虚拟 DOM 库,高效的,简单的;
Vue2 的 Vnode 就是借鉴这个库来编写的,Vue3 的 Diff 就更加强大了,颗粒度更精确,相对于 Vue2 性能也提升了很多。
Vue2 的新老节点替换规则(执行流程):
1. 对比新旧 DOM 的根节点是否相同,不相同则直接替换整个旧的 DOM 树,相等则下一步
2. 接下来 Vue 会遍历新旧虚拟 DOM 的子节点,比较节点的标签名是否相同
不同:替换整个节点及其子节点
相同:则下一步
3. 接下来,比较节点的属性,
不同则更新该属性
有新属性则插入新属性,
旧有新没有则移除旧的属性
4. 接下载比较节点的子节点
比较数量是否相同,不同则替换整个子节点
相同则逐个比较子节点
5. 比较子节点时 Vue 会采用两个指针的方式,分别从旧节点的首位开始遍历,相同则下一步,
不相同则比较上下节点是否有相同的:
有则将新节点移动到正确的位置,
没有则创建新节点,插入到旧节点的位置
# 简单手写代码解析
1. 搭建 webpack 环境
1 | npm init -y |
2. 引入 webpack 本地服务
1 | npm i webpack@4 webpack-cli@3 webpack-dev-erver@3 -S |
3. 配置入口出口文件 webpack.json
1 | module.exports = { |
4. 配置相关文件
1 | 1.public/index.html 单页面文件 |
5.index.js 编写简单的虚拟 DOM 用来测试手写的 diff 算法
1 | import { h } from './dom/h'//引入h函数 |
6. 手写 h 函数
1 | import vnode from './vnode'//引入转换虚拟DOM的函数 |
Vnode.js
1 | export default function (sel, data, children, params, elm) { |
7. 手写 patch 函数涉及 DOM 创建与删除等
1 | import vnode from './vnode'//虚拟节点转换 |
8.createElm.js 创建节点函数
1 | export default function createElm(vnode) { |
9.patchVnode.js 手写节点对比函数
1 | import createElm from "./createEle" |
10. 手写子节点对比函数 updateChildren.js (重点) diff 算法的精髓
1 | import patchVnode from "./patchVnode" |
11.package.json 配置函数
1 | { |
12.TIP: 如果有报错请使用 webpack@5 4 3 顺序的包
总结:
# 1.Vue2 和 Vue3diff 算法类型区别
- Diff 算法实现方式的区别:Vue2 使用了双指针的对比算法,而 Vue3 使用了更便捷的静态分析法
- 过程:Vue2 使用了深度优先的遍历算法,会遍历整个 Dom 进行比较更新;而 Vue3 使用了递归插入法,只对有变化的节点进行更新
- Patch 颗粒度:Vue2 的颗粒度是组件级别的,遍历整个 DOM 树进行比较;而 Vue3 的颗粒度是组件内节点级别地只会对比发生改变地节点;
- Key 值地处理:Vue2 中必须加入 Key 值才能进行比较,而 Vue3 使用了 Fragments 节点和片段节点,不需要再手动地指定 Key 属性,就可以进行比较更新
# diff 算法核心:再根节点相同,且都有子节点的情况
1. 旧前 -- 新前
相同:指针 ++ 继续比较下一个节点
不同:下一步
2. 旧后 -- 新后
相同:指针 -- 继续比较上一个节点
不同:下一步
3. 旧前 -- 新后
同:旧 ++,新 --
4. 旧后 -- 新前
同:旧 --,新 ++
5. 都不符合》
新中找旧,等位替换,查找到后赋值为 undefined