Vue 3:等待父级完成数据获取后再获取子级数据并显示加载程序

8

我正在寻找一种可重用的方法来显示全页加载器(侧边栏始终可见,但加载器应覆盖页面内容部分),直到完成所有必要的api获取。

我有一个父组件LaunchDetails包装在PageLoader组件中。

LaunchDetails.vue

<template>
  <PageLoader>
    <router-link :to="{ name: 'launches' }"> Back to launches </router-link>
    <h1>{{ name }}</h1>

    <section>
      <TabMenu :links="menuLinks" />
    </section>

    <section>
      <router-view />
    </section>
  </PageLoader>
</template>

<script>
import TabMenu from "@/components/general/TabMenu";

export default {
  data() {
    return {
      menuLinks: [
        { to: { name: "launchOverview" }, display_name: "Overview" },
        { to: { name: "launchRocket" }, display_name: "Rocket" },
      ],
    };
  },
  components: {
    TabMenu,
  },
  created() {
    this.$store.dispatch("launches/fetchLaunch", this.$route.params.launch_id);
  },
  computed: {
    name() {
      return this.$store.getters["launches/name"];
    },
  },
};
</script>

PageLoader.vue

<template>
  <Spinner v-if="isLoading" full size="medium" />
  <slot v-else></slot>
</template>

<script>
import Spinner from "@/components/general/Spinner.vue";

export default {
  components: {
    Spinner,
  },
  computed: {
    isLoading() {
      return this.$store.getters["loader/isLoading"];
    },
  },
};
</script>

LaunchDetails 模板中有另一个路由视图,在这些子页面中,根据 LaunchDetails 请求的数据进行新的获取请求。

RocketDetails.vue

<template>
  <PageLoader>
    <h2>Launch rocket details</h2>

    <RocketCard v-if="rocket" :rocket="rocket" />
  </PageLoader>
</template>

<script>
import LaunchService from "@/services/LaunchService";
import RocketCard from "@/components/rocket/RocketCard.vue";

export default {
  components: {
    RocketCard,
  },
  mounted() {
    this.loadRocket();
  },
  data() {
    return {
      rocket: null,
    };
  },
  methods: {
    async loadRocket() {
      const rocket_id = this.$store.getters["launches/getRocketId"];

      if (rocket_id) {
        const response = await LaunchService.getRocket(rocket_id);
        this.rocket = response.data;
      }
    },
  },
};
</script>

我需要的是一种在父组件(LaunchDetails)中提取数据的方法。如果这些数据存储在vuex store中,子组件(LaunchRocket)将获取必要的store数据并执行提取请求。在此期间,我想要全屏加载器或者在父组件加载时显示全屏加载器,以及包含嵌套画布的加载器。
此时,vuex store正在跟踪一个由axios拦截器处理的isLoading属性。
所有代码都可以在此沙箱中查看。
(注意:在此示例中,我可以从URL中获取rocket_id,但在我的项目中不是这样,所以我真的正在寻找一种从vuex store中获取这些数据的方法。)
2个回答

4

我来介绍一下你的救星——Suspense,这是一个与Vue v3一起添加的实验性功能。它的基本工作原理是,在父组件中创建一个悬挂点,当应用程序中任何深度的组件都被解析时,可以显示加载界面。请注意,你的组件应该是异步组件,这意味着它应该是延迟加载或将设置函数(composition api)设置为异步函数,以便返回异步组件。通过这种方式,你可以在子组件中获取数据,并在父组件中必要时显示后备内容。

更多信息:https://vuejs.org/guide/built-ins/suspense.html#suspense


2
你可以使用事件:

var Child = Vue.component('child', {
  data() {
    return {
      isLoading: true
    }
  },
  template: `<div>
    <span v-if="isLoading">Loading &hellip;</span>
    <span v-else>Child</span>
  </div>`,
  created() {
    this.$parent.$on('loaded', this.setLoaded);
  },
  methods: {
    setLoaded() {
      this.isLoading = false
    }
  }
});

var Parent = Vue.component('parent', {
  components: { Child },
  data() {
    return {
      isLoading: true
    }
  },
  template: `<div>
    Parent
    <Child />
  </div>`,
  mounted() {
    let request1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 1000);
    });
    let request2 = new Promise((resolve, reject) => {
      setTimeout(resolve, 2000);
    });
    Promise.all([ request1, request2 ]).then(() => this.$emit('loaded'))
  }
});

new Vue({
  components: { Parent },
  el: '#app',
  template: `<Parent />`
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

这可能被认为是一种反模式,因为它将父元素与子元素耦合在一起,并且事件被认为是相反的方向发送的。如果您不想使用事件来实现此功能,则监视属性也可以正常工作。在Vue 3中,非父子事件发射已被删除,但可以使用外部库来实现

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接