我们还是打开之前的案例
然后 将src下的index.js代码修改如下
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("ul",{},[
h("li",{},"a"),
h("li",{},"b"),
h("li",{},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
首先 我们写入节点的方法叫 patch
我们来查一下这个单纯的意思
其实 他不是一个暴力装卸的方法 而是 修补的一个概念
因为 我们需要一个触发事件的工具 所以 我们在www文件夹下的index.html中加一个id为btn的按钮
参考代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = "btn">更改dom</button>
<div id="container"></div>
<script src="/xuni/bundle.js"></script>
</body>
</html>
然后我们改写 src下的index.js代码如下
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("ul",{},[
h("li",{},"a"),
h("li",{},"b"),
h("li",{},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
//定义第二个虚拟dom节点
var vonm2 = h("ul",{},[
h("li",{},"a"),
h("li",{},"b"),
h("li",{},"c"),
h("li",{},"d")
]);
//获取btn节点
const btn = document.getElementById('btn');
//监听用户点击按钮
btn.onclick = function() {
//属性替换
patch(vonm,vonm2);
}
这里 我们获取了 btn 按钮 给他定义了一个点击事件 点击他 做了一个patch操作
这就是patch的神奇之后 不需要再传节点了 直接传两个需要比较的变量即可
然后我们运行项目
我们可以看到 现在运行的是 vonm的 abc
我们点击上面按钮
可以看到 这里就变成了 vonm2的 abcd
这里 我们要清除 不是直接暴力的 把vonm2又渲染上去了 而是对比两者差异 哦 发现只有一个 d不一样 就追加了一个d上去
这是patch的意义
那么 怎么证明呢
我们刷新界面
然后在控制台修改节点的文本
我们直接在控制台上 通过节点编辑 将a 改成丑八怪
然后 我们再次点击按钮 如果他是重新渲染 那么 这个a是不是该变回去啊?
那我们点击按钮
很显然 d出来了 但a没有复原 因为在程序中 他们确认过 第一个节点是一样的 我们在控制台改了内容js并不知道
所以 这就是 diff的好处 最小量更新
但你如果以为他是在最后面插入 那么我们就要思考另一个问题
它怎么实现前面或中间的更改呢?
我们将 src 下的 index.js代码更改如下
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("ul",{},[
h("li",{},"a"),
h("li",{},"b"),
h("li",{},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
//定义第二个虚拟dom节点
var vonm2 = h("ul",{},[
h("li",{},"d"),
h("li",{},"a"),
h("li",{},"b"),
h("li",{},"c")
]);
//获取btn节点
const btn = document.getElementById('btn');
//监听用户点击按钮
btn.onclick = function() {
//属性替换
patch(vonm,vonm2);
}
这次 我们把D放到最前面
我们运行之后 我们在上面 将 a 改成 丑八怪 b 改成 搭好
我们点击按钮
会发现 它既然变成了 dabc
其实 也不是重新渲染 但也差的不多 他把 a 变成了 d , b变成了 a ,c变成了 b 在最后面 又追加了一个c
这里证明的 是因为 前面的也被改了 丑八怪和搭好就被覆盖了
可能 这时 有小伙伴就会想 这也不是很智能啊
不是哦 是我们没有给他加key
我们将 src下的index.js入口文件修改如下
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("ul",{},[
h("li",{key: "a"},"a"),
h("li",{key: "b"},"b"),
h("li",{key: "c"},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
//定义第二个虚拟dom节点
var vonm2 = h("ul",{},[
h("li",{key: "d"},"d"),
h("li",{key: "a"},"a"),
h("li",{key: "b"},"b"),
h("li",{key: "c"},"c")
]);
//获取btn节点
const btn = document.getElementById('btn');
//监听用户点击按钮
btn.onclick = function() {
//属性替换
patch(vonm,vonm2);
}
这次 我们给这些虚拟节点加上了 key属性 然后我们再次运行项目
我们还是像之前一样 在控制台改一下a b 节点的文本
然后我们点击按钮 改变一下dom
可以很明显的看到 这次他就没动 a b 节点了 因为我们控制台改的内容并没有被覆盖 说明 这两个节点它没动
只是在最上面加了一个d 这就是key对虚拟dom的作用
key是节点的唯一标识 他能帮助我们完成真正意义上的最小量更新
然后 我们来看第二个东西
将 src下的 index.js代码修改如下
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("ul",{},[
h("li",{key: "a"},"a"),
h("li",{key: "b"},"b"),
h("li",{key: "c"},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
//定义第二个虚拟dom节点
var vonm2 = h("ol",{},[
h("li",{key: "a"},"a"),
h("li",{key: "b"},"b"),
h("li",{key: "c"},"c")
]);
//获取btn节点
const btn = document.getElementById('btn');
//监听用户点击按钮
btn.onclick = function() {
//属性替换
patch(vonm,vonm2);
}
这次 我们不改里面的li标签了 我们直接将他们的父节点 重 ul无序列表改为 ol 有序列表
那最小量更新就是 改一下父标签 对吗?
那么 我们来试一下是不是这样
我们运行项目 就还是在控制台把文本改了
然后 我们点击更新dom
可以看到 确实是边有序列表了 但 有没有发现 子节点也被更新了啊?
简单说 只有是同一个节点 才进行精细化 比较 简单说 他在接到这个 ul改ol时 已经视为 两者不是同一个节点了 那就直接暴力拆掉 然后重新装上去
等于 把整个全换了
当 选择器(就是标签类型) 或者 key不同时 他就会视为不是同一个节点
那可能有人会说 那这最小化更新不厉害啊?
但你们仔细想一下 在正式开发中 你们会遇到需要改父节点标签类型的逻辑吗? 所以 这是无伤大雅的 不要做完美主义
所以 这里写的所有内容 都是在vue中会发生的 父节点选择器改变时 子节点是不精细化校验更新的 他直接就旧的拆掉 新的上树 简单说 整个从内到外全换新
面试官面前 这些都可以聊
还有一个经常在文章和公众号文档中出现的 一个概念 只有同层才进行比较 但说的都很笼统 我们来好好研究一下
比如 我们这里 将src/index.js代码修改如下
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("div",{},[
h("p",{key: "a"},"a"),
h("p",{key: "b"},"b"),
h("p",{key: "c"},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
//定义第二个虚拟dom节点
var vonm2 = h("div",{},[
h("p",{key: "a"},"a"),
h("p",{key: "b"},"b"),
h("p",{key: "c"},"c")
]);
//获取btn节点
const btn = document.getElementById('btn');
//监听用户点击按钮
btn.onclick = function() {
//属性替换
patch(vonm,vonm2);
}
我们将他从div下三个p 改为一模一样的内容 然后我们点击按钮更新
会发现控制台没什么反应 说明 他判断 都是一样的 就不更新了
但我们将 src/index 下的代码更改成这样
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("div",{},[
h("p",{key: "a"},"a"),
h("p",{key: "b"},"b"),
h("p",{key: "c"},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
//定义第二个虚拟dom节点
var vonm2 = h("div",{},
h("div",{},[
h("p",{key: "a"},"a"),
h("p",{key: "b"},"b"),
h("p",{key: "c"},"c")
])
);
//获取btn节点
const btn = document.getElementById('btn');
//监听用户点击按钮
btn.onclick = function() {
//属性替换
patch(vonm,vonm2);
}
我们在更改后端内容内多加一层div
这里 我们刚运行项目 这里 节点结构明显是div下三个p
然后点击 他就变成了 div下一个div 下面三个p
那么 我们思考 他是否是最小量更新?
最小量更新就是 不要动三个p 在外面多套个div
可以先告诉大家 这个想法太美好了 但现实比较残酷
我们还是 文本测试法
我们在控制台上更改三个p的文本
然后点击按钮
让人失望的是 它全部覆盖了
说明的很简单 他把原本的三个p干掉了 然后重新插入了 div里面重写写入了三个p
意思就是 他只有同层会比较 就算你没有换标签 只要 他们层级不一样 也不会再比较了 也就没有精细化比较了
暴力拆掉旧的 强行插入新的
这个操作 我觉得会比上面那个换父标签类型可能更现实一点 但也基本遇不到 所以 没必要做完美主义 人家做开源还懒得做兼容呢 没什么人弄的东西 没必要强求
但同层 就算乱序 也可以最小量比较更新
我们将 src/index代码更改如下文章来源:https://www.toymoban.com/news/detail-439975.html
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
//创建patch函数
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);
//创建虚拟节点
var vonm = h("div",{},[
h("p",{key: "a"},"a"),
h("p",{key: "b"},"b"),
h("p",{key: "c"},"c")
]);
//让虚拟节点上树
const container = document.getElementById('container');
patch(container,vonm);
//定义第二个虚拟dom节点
var vonm2 = h("div",{},[
h("p",{key: "b"},"b"),
h("p",{key: "c"},"c"),
h("p",{key: "a"},"a")
]
);
//获取btn节点
const btn = document.getElementById('btn');
//监听用户点击按钮
btn.onclick = function() {
//属性替换
patch(vonm,vonm2);
}
我们这里 前后节点没有结构变化 只是改变了一下 abc的顺序
然后我们运行项目
还是在控制台更改一下内容
然后点击按钮更新dom
可以看到 并没有强行去拆 而是做了最小量的比较更新 只是换了顺序 我们更改的文本还在 说明节点没有换
这就是patch内部算法的强大
那么 我之后也会出问题讲一下 patch 并带大家手写一个自己的 patch
好啦 那就到这啦文章来源地址https://www.toymoban.com/news/detail-439975.html
到了这里,关于vue diff算法与虚拟dom知识整理(6) 感受diff算法 (不要神话虚拟dom更不要做完美主义)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!