vnode结构转换成真实dom
由于文章是本人在技术分享前准备的, 部分内容没有直接体现在文档上, 到最后大部分内容都是纯靠讲述了, 所以文章缺失了很多讲解和分析, 有空再来补上吧。
通过 snabbdom 学习vdom
在数据更新过程中的diff 比较
查看 h 函数
Patch 函数、patchVnode函数、updateChildren函数
diff算法的特点
时间复杂度从 O(n^3 ) 到 O(n)。
O(n^3 ) 的话,1000个节点就要计算1亿次,算法不可用
- 只比较同级节点,不跨级比较
- Tag 不相同,则直接删掉重建,不再深度比较
- Tag 和 key 两者都相同,则认为是相同节点,则向下比较
6,示例:不加正确的key会产生哪些问题?
<template>
<div id="app">
<div v-for="(item, index) in data" :key="index">
<Child :key="index"/>
<button @click="handleDelete(index)">删除这一行</button>
</div>
</div>
</template>
<script>
import Child from "./Child";
export default {
name: "App",
components: {
Child
},
data() {
return {
data: [
{ name: "小明" },
{ name: "小红" },
{ name: "小蓝" },
{ name: "小紫" },
]
};
},
methods: {
handleDelete(index) {
this.data.splice(index, 1);
},
}
};
</script>
<template>
<span>{{name}}{{Math.floor(Math.random() * 1000)}}</span>
</template>
<script>
export default {
name: "Child",
props: {
name: {
type: String,
default: ''
}
}
}
</script>
模拟上述template的dom结构
[
{
tag: 'div',
key: 0,
children: [
{
tag: VueComponent,
elm: 408, // 这个Vnode对应的真实dom是408
},
{
tag: 'button'
}
]
},
{
tag: 'div',
key: 1,
children: [
{
tag: VueComponent,
elm: 227, // 这个Vnode对应的真实dom是227
},
{
tag: 'button'
}
]
}
...
]
删除第一条数据后,新的dom结构为
[
{
tag: 'div',
key: 0,
children: [
{
tag: VueComponent,
elm: 227, // 这个Vnode对应的真实dom是227
},
{
tag: 'button'
}
]
},
{
tag: 'div',
key: 1,
children: [
{
tag: VueComponent,
elm: 324, // 这个Vnode对应的真实dom是324
},
{
tag: 'button'
}
]
}
...
]
由于 key 都是0,所以比较第一条的时候,就会命中 sameNode ,导致错误复用, 然后 updateChildren ,子节点的 Vnode 依然会命中 sameVnode , 同理,第二、三条均会命中 sameVnode ,而直接错误复用其关联的真实 dom 节点, 所以我们明明删除的是第一条,UI表现却是最后一条被删除了。
扩展思考:为什么日常用 index 作为 key 时没出现问题?
这次, 我们尝试将name属性传入其中, 删除第二条
<template>
<div id="app">
<div v-for="(item, index) in data" :key="index">
<Child :name="`${item.name}`" />
<button @click="handleDelete(index)">删除这一行</button>
</div>
</div>
</template>
因为key 一致,新的 Vnode 数组依然会复用旧的 Vnode 数组的前三条, 第一条 Vnode 是正确复用,组件的 propsData 未发生变化,不会触发 update , 直接复用其关联的真实 dom 节点,但是第二条 Vnode 是错误复用,但是组件的 propsData 发生变化, 由小红变成了小蓝,触发了 update ,组件重新渲染, 因此我们看到其实连 random 都发生了变化,第三条同理。