结论:
下面讲针对结论1和2进行简单的讲述(前提: 需要已经掌握了vue3的diff算法, 本文中没有对diff算法做解释,所以之前没有看过或者了解过diff算法的可能不适合本篇文章)。
1. 逆序添加或者删除的时候,即在集合头部添加或者删除, 会使所有new key和old key不一致,就会产生所有的dom重新渲染的情况,和没有key的效率没有啥区别。
2. 同时使用index作为key还有产生一些bug,当使用输入类的dom时,会产生错误的dom更新
先看一下没有绑定key的情况。
html:
- <ul class="flex" v-for="item in state.tableData1">
- <li>{{item.id}}li>
- <li class="ml-20">{{item.name}}li>
- <input class="ml-10" />
- <el-input class="width-200 ml-10" v-model="item.inputValue" placeholder="请输入">el-input>
- ul>
- <el-button @click="onTestClick">测试el-button>
js部分代码:
- const state = reactive({
- tableData1: [{
- id: 1,
- name: 'james1',
- inputValue: ''
- },{
- id: 2,
- name: 'james2',
- inputValue: ''
- },{
- id: 3,
- name: 'james3',
- inputValue: ''
- }]
- })
-
- const onTestClick = () => {
- state.tableData1.unshift({ id: state.tableData1.length + 1, name: `james${state.tableData1.length + 1}`, inputValue: '' })
- }
在输入框里输入相应的值之后,界面效果如下:
点击测试按钮之后,界面效果如下图所示;
由于elementpuls组件的v-model的原因,所有的dom节点在错位后会根据具体的值重新赋值, 但是原生的输入框没有双向绑定,所以就会保持原始的dom节点上的值,由此可见,如果没有双向绑定就会出现值的错误,有双向绑定之后,由于所有的dom重新渲染赋值,界面的效率会受到影响。
下面来看一下使用index作为key会不会也有这种情况:
HTML
- <ul class="flex" v-for="(item, index) in state.tableData1" :key="index">
- <li>{{item.id}}li>
- <li class="ml-20">{{item.name}}li>
- <input class="ml-10" />
- <el-input class="width-200 ml-10" v-model="item.inputValue" placeholder="请输入">el-input>
- ul>
- <el-button @click="onTestClick">测试el-button>
JS部分保持和上文的js一致
点击测试按钮后效果图如下:
和没有绑定key时一样的问题。
下面来看一下绑定了唯一固定的值作为key的情况
HTML
- <ul class="flex" v-for="item in state.tableData1" :key="item.id">
- <li>{{item.id}}li>
- <li class="ml-20">{{item.name}}li>
- <input class="ml-10" />
- <el-input class="width-200 ml-10" v-model="item.inputValue" placeholder="请输入">el-input>
- ul>
- <el-button @click="onTestClick">测试el-button>
效果如下:
附件:diff算法的简单梳理:
经过上述我们大致知道了diff算法的流程
1 从头对比找到有相同的节点 patch ,发现不同,立即跳出。
2如果第一步没有patch完,立即,从后往前开始patch ,如果发现不同立即跳出循环。
3如果新的节点大于老的节点数 ,对于剩下的节点全部以新的vnode处理( 这种情况说明已经patch完相同的vnode )。
4 对于老的节点大于新的节点的情况 , 对于超出的节点全部卸载 ( 这种情况说明已经patch完相同的vnode )。
5不确定的元素( 这种情况说明没有patch完相同的vnode ) 与 3 ,4对立关系。
1 把没有比较过的新的vnode节点,通过map保存
记录已经patch的新节点的数量 patched
没有经过 path 新的节点的数量 toBePatched
建立一个数组newIndexToOldIndexMap,每个子元素都是[ 0, 0, 0, 0, 0, 0, ] 里面的数字记录老节点的索引 ,数组索引就是新节点的索引
开始遍历老节点
① 如果 toBePatched新的节点数量为0 ,那么统一卸载老的节点
② 如果,老节点的key存在 ,通过key找到对应的index
③ 如果,老节点的key不存在
1 遍历剩下的所有新节点
2 如果找到与当前老节点对应的新节点那么 ,将新节点的索引,赋值给newIndex
④ 没有找到与老节点对应的新节点,卸载当前老节点。
⑤ 如果找到与老节点对应的新节点,把老节点的索引,记录在存放新节点的数组中,
1 如果节点发生移动 记录已经移动了
2 patch新老节点 找到新的节点进行patch节点
遍历结束
如果发生移动
① 根据 newIndexToOldIndexMap 新老节点索引列表找到最长稳定序列
② 对于 newIndexToOldIndexMap -item =0 证明不存在老节点 ,从新形成新的vnode
③ 对于发生移动的节点进行移动处理。