最近在写一个列表组件,其中有一个功能是查询条件发生改变时,清空列表数据,重新请求第一页。简化版代码如下:
<!--TestList.vue-->
<template>
<p>{{ search }}</p>
<p>{{ list }}</p
</template>
<script>
export default {
props: {
// 查询条件
search: {
type: Object,
default: () => ({}),
},
// 列表数据由外部传入,因为外部有时候需要主动增删一些数组元素
list: {
type: Array,
default: () => [],
},
},
watch: {
// 当search发生改变时,重新加载页面
search() {
this.list.splice(0)
this.loadData()
},
},
mounted() {
// 加载第一页
this.loadData()
},
methods: {
loadData() {
setTimeout(() => {
// 根据查询条件查询数据
const data = fetchData(this.search)
// 将数据添加到list末尾
data.forEach(ele=>{
this.list.push(ele)
})
}, 10)
},
},
}
</script>
然后我这样使用这个组件:
<!--App.vue-->
<template>
<TestList :search="{}" :list="list"/>
</template>
<script>
export default {
data() {
return {
list: []
}
}
}
</script>
于是页面就卡死了。原因在于search传入了一个对象字面量。产生错误的原理是这样的:
- 执行mounted,调用loadData()获取数据。
- 执行到
this.list.push(ele)
时,list被TestList.vue变更,导致在下一个tick视图触发更新,App.vue重新调用render()函数刷新虚拟DOM,search的值被一个新的对象替换,从而导致TestList.vue中的search watch被触发,再次改变了list,陷入了死循环。
只要把赋给TestList.vue的search prop的对象挂到App.vue上,保证视图更新前后的search prop是同一个对象就行了。
<!--App.vue-->
<template>
<TestList :search="search" :list="list"/>
</template>
<script>
export default {
data() {
return {
search: {},
list: []
}
}
}
</script>
这个bug的原因大概找了4个小时。
标签:search,vue,default,list,loadData,props,卡死,data,页面 From: https://www.cnblogs.com/hdxg/p/16753150.html