• 欢迎光临~

错误地使用props导致页面卡死

开发技术 开发技术 2022-10-04 次浏览

最近在写一个列表组件,其中有一个功能是查询条件发生改变时,清空列表数据,重新请求第一页。简化版代码如下:

<!--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传入了一个对象字面量。产生错误的原理是这样的:

  1. 执行mounted,调用loadData()获取数据。
  2. 执行到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个小时。

程序员灯塔
转载请注明原文链接:错误地使用props导致页面卡死
喜欢 (0)
违法和不良信息举报电话:022-22558618 举报邮箱:dljd@tidljd.com