在 vue.js 中渲染子组件之前等到父组件安装/准备好

Posted

技术标签:

【中文标题】在 vue.js 中渲染子组件之前等到父组件安装/准备好【英文标题】:Wait until parent component is mounted / ready before rendering child in vue.js 【发布时间】:2018-07-23 19:53:38 【问题描述】:

在我的 SPA 应用程序中,我有一个 <app-view> 包装器,它处理基本应用程序代码(加载用户数据、渲染导航栏和页脚等),并有一个 slot 用于渲染实际页面。此仅当用户数据可用时才会呈现此槽

创建此包装器是因为某些页面需要不同的基本代码,因此我无法再将此基本代码保留在包含 <router-view> 的主应用程序中。

我尝试查看 vue-router 是否提供高级选项或建议用于切换基本代码的设计模式,但没有找到任何东西。

问题是子组件会在父组件挂载之前渲染,即在父组件决定不渲染子组件之前(因为它正在加载用户数据)。这会导致像undefined as no attribute foo 这样的错误。

正因为如此,我正在寻找一种方法来推迟子渲染,直到它的父被挂载。

【问题讨论】:

你试过v-cloak吗? 不,但不只是为了在 vue 准备好之前隐藏 html 模板吗?我使用的是 vue 模板,而不是 html。 我没有尝试将v-if 放入<slot> 标记中。我想知道...? 【参考方案1】:

我遇到了类似的问题,但不是 SPA。我有需要来自父级的数据的子组件。问题是只有在父级完成安装后才会生成数据,所以我最终在子级中得到了空值。

我就是这样解决的。我仅在父级完成安装后才使用v-if 指令安装子级。 (在mounted() 方法中)见下面的例子

<template>
  <child-component v-if="isMounted"></child-component>
</template>
<script>
  data() 
     isMounted: false
  , mounted() 
     this.isMounted = true
  
</script>

之后,孩子可以从父母那里获取数据。 这有点无关,但我希望它能给你一个想法。

【讨论】:

【参考方案2】:

对于想要在父组件从 API 调用中获取数据后立即显示子组件的任何人,您应该使用以下内容:

<template>
  <child-component v-if="itemsLoaded"></child-component>
</template>

<script>
  data() 
     itemsLoaded: false
  ,
  methods: 
      getData() 
          this.$axios
              .get('/path/to/endpoint')
              .then((data) => 
                  // do whatever you need to do with received data
                  
                  // change the bool value here
                  this.itemsLoaded = true
          )
          .catch((err) => 
              console.log(err)
          )
      ,
  , 
  mounted() 
     this.getData()

     // DONT change the bool value here; papa no kiss
     this.itemsLoaded = true
  
</script>

如果你尝试改变mounted()方法中的布尔值this.itemsLoaded = true,在调用getData()方法之后,你会得到不一致的结果,因为你可能会也可能不会收到this.itemsLoaded = true之前的数据执行。

【讨论】:

另外,mounted() 方法可以设为异步 (async mounted()),以便您可以 await this.getData() 并以您警告的方式设置 this.itemsLoaded = true【参考方案3】:

您实际上可以将v-if 放在组件中的&lt;slot&gt; 标记上。

new Vue(
  el: '#app',
  render: function(createElement) 
    return createElement(
      // Your application spec here
      
        template: `<slotty :show="showSlot"><span> here</span></slotty>`,
        data() 
          return 
            showSlot: false
          
        ,
        components: 
          slotty: 
            template: `<div>Hiding slot<slot v-if="show"></slot>.</div>`,
            props: ['show']
          
        ,
        mounted() 
          setTimeout(() => this.showSlot = true, 1500);
        
      
    );
  
)
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
</div>

【讨论】:

这在我的情况下不起作用,因为即使我不渲染插槽(例如,我在“加载”v-if 分支中,并且插槽位于不同的分支)【参考方案4】:

在尝试了几个选项之后,看来我需要硬着头皮明确定义我的组件所依赖的数据,如下所示:

<app-view>
  <div v-if='currentProfile'>
    ...
  </div>
</div>

(currentProfile 是从 vuex store getter 接收的,并在 app-view 内获取)

【讨论】:

以上是关于在 vue.js 中渲染子组件之前等到父组件安装/准备好的主要内容,如果未能解决你的问题,请参考以下文章

初学Vue.js:props

VueJS - 在渲染子组件之前等待父组件中的更新()

从 vue.js 3 中传递数据的方法渲染组件

在 vue.js 组件中渲染子组件

如何在父组件中使用来自 Vue.js 子组件的数据?

Vue.js 中父级数据更改时如何重新渲染内部组件