一. VNode
div,body等标签渲染到浏览器中的是真实节点
VNode,虚拟节点,存在于内存中的JavaScript对象,组件还是元素,最终在Vue中表示出来的都是一个个VNode。本质就是JavaScript对象。
比如组件里面的<template>中的内容里面的标签,都是一个个VNode。
image.png
Vue解析<template>中的内容里面的标签和元素的时候,然后会把那里面的每一个标签生成VNode,然后再通过一系列操作,生成真实dom。
image.png
Vnode的好处就是做多平台的适配,渲染。可以做服务端的渲染,移动端的渲染,浏览器的渲染
二.虚拟Dom
VNode形成了一个树结构,这个树结构这就是虚拟DOM(Virtual DOM).
对虚拟dom进行渲染之后,就变成了真实dom。
三.新旧DOM的对比
比如
<!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>
<div id="app"></div>
<template id="my-app">
<ul>
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
<button @click="insertF">插入F元素</button>
</template>
<script src="../js/vue.js"></script>
<script>
const App = {
template: '#my-app',
data() {
return {
letters: ['a', 'b', 'c', 'd']
}
},
methods: {
insertF() {
this.letters.splice(2, 0, 'f')
}
}
}
Vue.createApp(App).mount('#app');
</script>
</body>
</html>
'a', 'b', 'c', 'd' --》'a', 'b', 'f','c', 'd'
// 在位置 2,添加 f元素:
diff算法:新的Vnode和旧的Vnode比较的过程,就是diff算法。
Vue事实上会对于有key和没有key会调用两个不同的方法;
1. 没有key的时候
一个一个Vnodes里面的东西比较
旧的Vnodes第一个里面的值a和新的a比较,一样,不变
旧的b和新的b比较,一样,不变
旧的c和新的f比较, 不一样,把c替换成f,
旧的d和新的c比较,不一样,把d替换成 c
新的里面还有一个d,就新增一个d
以上的性能比较低
image.png
2.有key的时候
从头部还是while遍历
判断旧的第一个元素的type和key是不是和新的第一个元素的type和key是不是一样的。
只要是一样的,就比较下一个,
如果下一个不一样,就跳出while循环遍历。
然后从尾部开始遍历。
'a', 'b', 'c', 'd' --》
'a', 'b', 'f','c', 'd'
这时候d和d比较,相同,就往前一个比较
c和c比较,相同,就往前一个比较
然后b和f比较,key不一样,跳出循环
如果新节点比较多,就和旧的空节点比较,对新的增加进来的进行挂载。
还剩一个f,就挂载进来,f就是一个新增的节点
如果是旧节点比较多,就是unmount,旧直接把旧的节点卸载掉
四:总结
所以我们可以发现,Vue在进行diff算法的时候,会尽量利用我们的key来进行优化操作,也就是尽量复用我们已将有的dom元素:
- 在没有key的时候我们的效率是非常低效的;
- 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效;
网友评论