Vue子组件在第一页加载时不显示动态数据

Posted

技术标签:

【中文标题】Vue子组件在第一页加载时不显示动态数据【英文标题】:Vue child component not displaying dynamic data on first page load 【发布时间】:2021-10-28 19:18:34 【问题描述】:

鉴于下面的代码,我的子组件警报在父挂载函数中的任何代码之前触发。

因此,在数据准备好之前,孩子似乎已经完成了初始化,因此在重新加载之前不会显示数据。

当原始 JSON 显示在布局中的 v-card 内时,数据本身可以从 API 中正常返回。

我的问题是如何确保在子组件加载之前,父组件中请求的数据已准备好?我发现的所有内容都集中在使用 props 传入的静态数据上,但是当必须首先获取数据时,这似乎完全失败了。

在父级的mounted() 中,我有这段代码用于检索数据。

const promisesArray = [this.loadPrivate(),this.loadPublic()]
      await Promise.all(promisesArray).then(() => 
      console.log('DATA ...') // fires after the log in Notes component
      this.checkAttendanceForPreviousTwoWeeks().then(()=>
        this.getCurrentParticipants().then((results) =>      
                  this.currentP = results
                  this.notesArr = this.notes // see getter below   
        )

在父级中检索数据的getter

  get notes() 
    const newNotes = eventsModule.getNotes
    return newNotes
  

我在父模板中的组件:

<v-card light elevation="">
     notes  // Raw JSON displays correctly here
   // Passing the dynamic data to the component via prop
   <Notes v-if="notes.length" :notesArr="notes"/>
</v-card>

子组件:

...
  // Pickingn up prop passed to child
  @Prop( type: Array, required: true )
      notesArr!: object[]

  constructor()
    
      super();    
      alert(`Notes : $this.notesArr`) // nothing here 
      this.getNotes(this.notesArr)    
    

 async getNotes(eventNotes)
    // THIS ALERT FIRES BEFORE PROMISES IN PARENT ARE COMPLETED
    alert(`Notes.getNotes CALL.. $eventNotes`) // eventNotes = undefined
    this.eventChanges = await eventNotes.map(note => 
      return 
        eventInfo: 
          name: note.name,
          group: note.groupNo || null,
          date: note.displayDate,
        ,     
        note: note.noteToPresenter
      
    )
  
...

我对 Vue 比较陌生,所以如果我忽略了一些基本的东西,请原谅我。我已经尝试修复它几天了,但无法弄清楚,因此非常感谢任何帮助!

【问题讨论】:

看来您使用的是vue-class-component ....正确? 【参考方案1】:

如果您是 Vue 新手,我真的建议您阅读它的整个文档以及您正在使用的工具 - vue-class-component(这是 Vue 插件添加 API 用于将 Vue 组件声明为类)

    Caveats of Class Component - Always use lifecycle hooks instead of constructor

因此,您应该将代码移动到created() lifecycle hook,而不是使用constructor()

在这种情况下,这应该足以修复您的代码,但只是因为 Notes 组件的使用由父级中的 v-if="notes.length" 保护 - 只有在 notes 不是空数组后才会创建组件

这在很多情况下是不够的!

    created() 生命周期钩子(和 data() 函数/钩子)仅对每个组件执行一次。里面的代码是一次性初始化。因此,当/如果父组件更改 notesArr 道具的内容(有时在将来),eventChanges 将不会得到更新。即使你知道 parent 永远不会更新 prop,请注意,出于性能原因,Vue 倾向于在使用 v-for 或 switching between components of the same type 和 v-if/v-else 渲染列表时尽可能重用现有组件实例 - 而不是破坏现有的并创建新组件,Vue 只是更新道具。应用突然看起来无缘无故坏了...

这是许多没有经验的用户都会犯的错误。您可以在这里找到很多关于 SO 的问题,例如“我的组件没有反应性”或“如何强制我的组件重新渲染”,许多答案建议使用 :key hack 或使用观察者 ....有时可以工作但几乎总是比正确的解决方案复杂得多

正确的解决方案是将组件编写为pure components(如果可以 - 有时是不可能的)(文章适用于 React,但原则仍然适用)。在 Vue 中实现这一点的非常重要的工具是 computed propeties

因此,不要引入 eventChanges 数据属性(might or might not be reactive - 这从您的代码中不清楚),您应该将其设为直接使用 notesArr 属性的计算属性:

get eventChanges() 
   return this.notesArr.map(note => 
     return 
       eventInfo: 
         name: note.name,
         group: note.groupNo || null,
         date: note.displayDate,
       ,     
       note: note.noteToPresenter
     
   )

现在每当 notesArr 属性被父级更改时,eventChanges 就会更新并且组件将重新渲染

注意事项:

您过度使用async。您的 getNotes 函数不会执行任何异步代码,因此只需将其删除即可。 也不要混用 asyncthen - 这会令人困惑

要么:

const promisesArray = [this.loadPrivate(),this.loadPublic()]
await Promise.all(promisesArray)
await this.checkAttendanceForPreviousTwoWeeks()
const results = await this.getCurrentParticipants()
this.currentP = results
this.notesArr = this.notes

或:

const promisesArray = [this.loadPrivate(),this.loadPublic()]
Promise.all(promisesArray)
  .then(() => this.checkAttendanceForPreviousTwoWeeks())
  .then(() => this.getCurrentParticipants())
  .then((results) =>      
    this.currentP = results
    this.notesArr = this.notes
  )

Great learning resource

【讨论】:

非常感谢这位米哈尔。你给了我一些很好的指导,让我继续前进。我认为因为我正在实例化一个类,所以可以使用 constructor() 函数,就像普通的 JS 一样。但是,是的,我同意,vue-class-component 插件改变了一切。我将根据您的建议尝试重构整个组件,希望它能解决问题。再次感谢您的时间和精力。

以上是关于Vue子组件在第一页加载时不显示动态数据的主要内容,如果未能解决你的问题,请参考以下文章

Vue进阶-异步动态加载组件

Vue进阶-异步动态加载组件

vue滚动列表和其他组件联动效果,怎么实现呢?

React组建实现新闻下拉刷新加载

媒体查询在第一页加载时不起作用

UserList.vue组件中用钩子函数初始化第一页的分页数据