前言
今天有个小伙伴问了我一个他碰到的问题,他在动态添加数组操作的时候 会发生渲染错乱的问题,显示在页面和数组里面的num不一致,引发问题的demo,在点击前面几列的加号的时候 连续点击两次就会问题。如下图 应该为2、3却渲染成3、3(ps:这里不去讨论增减的方法之类,这里他直接改变了tripIndex 所以不能拿这个当做key值 data可能会一样 然后id是我后来加进去的,并不是一开始存在的,当做解决办法)
附上代码(只是一个简单的vue2和引用了element-ui)
<template>
<div class="con">
<div><el-button size='mini' @click='clickEvent'>测试</el-button></el-button></div>
<div style="width:1000px;padding:50px">
<el-table :data="tableData" border style="width: 100%" v-if='isShow'>
<el-table-column
v-for="(item, index) in tableHeader"
:key='item.id'
:prop="item.data"
width="100"
align="center"
>
<template slot="header" slot-scope="scope">
<div>
<i
@click="addHeade(item.tripIndex)"
class=" myIcon operateLine-item"
>+</i>
<i
@click="subHeade(item.tripIndex)"
class=" myIcon"
>-</i>
</div>
{{ `${item.tripIndex} ${getMain(item)} ` }}
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
export default {
name: "TestPage",
components: {},
data() {
return {
isShow:true,
tableHeader: [
{
tripIndex: 1,
data: "data",
id:1
},
{
tripIndex: 2,
data: "data",
id:2
},
{
tripIndex: 3,
data: "data",
id:'3'
},
{
tripIndex: 4,
data: "data",
id:'4'
},
{
tripIndex: 5,
data: "data",
id:'5'
}
],
tableData: [
{
data: "1"
}
]
};
},
mounted() {},
methods: {
clickEvent(){
console.log(this.tableHeader)
},
getMain(v) {
if (v.tripIndex % 2 == 0) {
return "副站";
} else {
return "主站";
}
},
addHeade(v) {
this.tableHeader.forEach(element => {
if (element.tripIndex > v) {
element.tripIndex += 1;
}
});
this.$nextTick(() => {
console.log(this.tableHeader);
});
console.log(this.tableHeader);
let addItem = {
tripIndex: v + 1,
data: "data",
};
console.log(v);
this.tableHeader.splice(v, 0, addItem);
},
subHeade(v) {
this.tableHeader.splice(v - 1, 1);
this.tableHeader.forEach(element => {
if (element.tripIndex > v) {
element.tripIndex -= 1;
}
});
}
},
created() {}
};
</script>
<style scoped>
.con {
overflow: auto;
position: relative;
width: 100%;
height: 100%;
}
.operateLine-item {
margin-right: 10px;
}
.myIcon {
color: #1890ff;
cursor: pointer;
}
</style>
解决
这种问题其实我有碰到类似的 所以当时就直接给了三个解决方案
- this.$forceUpdata()强制刷新 (不过貌似不能成功)
- 使用v-if强制刷新
3.增加一个唯一标识id,然后在v-for后指明这个id当做key值
:key='item.id'
...
let addItem = {
tripIndex: v + 1,
data: "data",
id: new Date().getTime()
};
这三种办法后两种是可行的 使用强制刷新重新渲染。 成功demo 虽然有这样的解决方法,但其实我也一知半解的状态所以我要深入思考一下
分析
这是我当时自己的判断
既然不懂 这时候就要开始百度一下了,这里附上一个很有意思的知乎类似问答 比如这里还有类似问题
复现demo连接 add三次 click后面两个 然后删除第一个就会有相似渲染问题
搜索到vue官网文档有一句话很有意思
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项
的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并
且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的 track-by="$index"。
“就地更新”的策略 这个词可能就让我们很接近答案,就地更新。
相关答案
我觉得需要模拟ast抽象树 去打印出对应结果(之后有时间来追踪一下)
小结
我自己的结论:diff算法导致的问题,而key值的存在是为了更好的去diff出AST抽象树,当没有key值就会使用就地更新的策略,如果key值混乱也会出现渲染问题。
大佬们要是有明白的又刚好看见此文,还望不吝指教!