Vue 道具没有在子组件中正确更新

Posted

技术标签:

【中文标题】Vue 道具没有在子组件中正确更新【英文标题】:Vue prop not updating properly in a child component 【发布时间】:2020-12-11 01:00:05 【问题描述】:

我有一个从父组件接收道具的子组件。我添加了一个事件来在单击按钮时更新父组件中的道具。但是子组件并没有检测到prop的变化。

大概代码:

父组件:

<template>
  <table>
    <Student v-for="(student, index) in students"
      :key="index"
      :student="student"
      :attendance="attendance[student.id]"
      @attended-clicked="changeAttendance"
    ></student>
  </table>
</template>

<script>
  export default 
    data() 
      return 
        students: [
          id: 1, name: 'Pepito',
          id: 2, name: 'Sue',
        ],
        attendance: 
          1: attended: true,
          2: attended: false,
        
      
    ,
    methods: 
      changeAttendance(studentId) 
        this.attendance[studentId].attended = !this.attendance[studentId].attended;
      
    
  
</script>

子组件:

<template>
  <tr>
    <td> student.name </td>
    <td><v-btn :color="attendance.attended ? 'green' : 'red'"
          @click="$emit('attended-clicked', student.id)"
        >Attended</v-btn>
  </tr>
</template>

<script>
  export default 
    props: ['student', 'attendance']
  
</script>

我已尝试使代码尽可能少,以便让您了解我想要实现的目标。期望是当按钮被点击时,attendance.attended 会发生变化。

我可以验证使用 Vue 开发人员工具单击时该值是否正在更改(尽管我必须按下“强制刷新”按钮才能看到更改)。但显然,孩子的观点并没有接受这种变化。

我在这里做错了什么会破坏反应性吗?

我也尝试过使用Vue.set 和一些类似这样的代码:

methods: 
  changeAttendance(studentId) 
    let attendance = this.attendance[studentId];
    attendance.attended = !attendance.attended;
    Vue.set(this.attendance, studentId, attendance);
  

没有区别。

感谢您对我做错的任何见解,谢谢!

【问题讨论】:

你能做一个sn-p 不知道如何用多个 vue 组件制作 sn-p 和 vuetify。不过我会调查的 studentsattendance 我猜应该是一个数组 @Chris 这是你需要的吗:s8o8q.csb.app? 这是sandbox 【参考方案1】:

根据 cmets,根本问题是 attendance 被设置为 ,这意味着根据 change detection part in the VueJS docs,attendance[student.id] 对象实际上并不存在或不再被跟踪。这就是问题中提供的Vue.set 示例代码应该做的事情。

基本上我看到了两种解决方法:

    将出勤设置为在 API 返回结果时始终使所有学生 ID 可用,从而使对象始终具有已经工作的对象 当您需要从 attendance 获取值时开始跟踪反应性。

由于我不知道 API 以及它是如何被调用的,我将为选项 2 提供答案。提供的代码几乎可以工作,它只需要对 Parent 组件进行两次更改,以确保学生考勤对象总是被填满,将来会被跟踪:

<template>
  <table>
    <!-- :attendance needs an object to work -->
    <Student
      v-for="(student, index) in students"
      :key="index"
      :student="student"
      :attendance="attendance[student.id] || "
      @attended-clicked="changeAttendance"
    />
  </table>
</template>

<script>
import Vue from "vue";
import StudentVue from "./Student.vue";

export default 
  data() 
    return 
      students: [ id: 1, name: "Pepito" ,  id: 2, name: "Sue" ],
      attendance: 
    ;
  ,
  methods: 
    changeAttendance(studentId) 
      // make sure to have an object when accessing a student!
      let attendance = this.attendance[studentId] || ;
      attendance.attended = !attendance.attended;
      // start tracking - this will always "reset" the tracking though!
      Vue.set(this.attendance, studentId, attendance);
    
  ,
  components:  Student: StudentVue 
;
</script>

由于this.attendance[student.id] || 被多次使用,我将其封装到getAttendance(studentId) ... 函数中。在那里,Vue.set 可用于在对象之前不存在的情况下开始跟踪,从而使changeAttendance 更简单一些,|| 的意图更明确。

<template>
  <table>
    <Student
      v-for="(student, index) in students"
      :key="index"
      :student="student"
      :attendance="getAttendance(student.id)"
      @attended-clicked="changeAttendance"
    />
  </table>
</template>

<script>
import Vue from "vue";
import StudentVue from "./Student.vue";

export default 
  data() 
    return 
      students: [ id: 1, name: "Pepito" ,  id: 2, name: "Sue" ],
      attendance: 
    ;
  ,
  methods: 
    changeAttendance(studentId) 
      let attendance = this.getAttendance(studentId);
      attendance.attended = !attendance.attended;
    ,
    getAttendance(studentId) 
      const studentAttendance = this.attendance[studentId];
      if (!studentAttendance) 
        Vue.set(this.attendance, studentId,  attended: false );
      
      return this.attendance[studentId];
    
  ,
  components:  Student: StudentVue 
;
</script>

如果代码库在其他组件中使用this.something[something]this.getAttendance 可能看起来有点不一致。在这种情况下,最好先将 API 调用的结果更改给所有学生,然后再将其提供给Parent 组件。就个人而言,我会尝试使用该选项。我会使用this.$set 而不是Vue.set 来摆脱import Vue 线... ;)

【讨论】:

以上是关于Vue 道具没有在子组件中正确更新的主要内容,如果未能解决你的问题,请参考以下文章

Vue - 组件道具没有正确观察对象变化

Vue props 数据未在子组件中更新

Vue.js - 如何将 prop 作为 json 数组传递并在子组件中正确使用?

承诺解决并将道具传递给嵌套子项时如何更新Vue组件

如何在子组件和父组件之间捕获或同步道具值?

Vue父组件道具没有传递给子组件