Можете воспользоваться suspense(vue3) или написать реализацую v-if и  + observer,
Пример простой реализации: 
Компонент Skeleton
<template>
  <div class="lazy-load__component">
    <component v-intersection="handleShowComponent" v-if="!isComponentVisible" :is="skeleton" />
    <template v-else>
       <slot />
   </template>
  </div>
</template>
<script>
export default {
  components: {
    allSkeletons: () => import("path/to/skeletons")
  },
  directives: {
    intersection: {
      inserted: (el, binding) => {
        window.addEventListener("load", () => {
          const options = {
          rootMargin: "0px",
          threshold: 1
        };
        const callback = entries => {
          if (entries[0].isIntersecting) {
            binding.value();
            observer.unobserve(el);
          }
        };
        const observer = new IntersectionObserver(callback, options);
        observer.observe(el);
        });
      },
    }
  },
  },
  props: {
     skeleton: {
        type: String,
        default: 'default-skeleton'
     }
  },
  data() {
    return {
      isComponentVisible: false,
    };
  },
  computed: {
    skeleton() {
      return allSkeletons[this.skeleton];
    },
  },
  methods: {
    handleShowComponent() {
      this.isComponentVisible = true;
    },
  },
};
</script>
Компонент с использованием Skeleton
<template>
  <Skeleton skeleton="someSkeleton">
      <div>
        Some content
      </div>
  </Skeleton>
</template>
<script>
export default {
  components: {
    Skeleton: () => import("path/to/skeleton")
  }
};
</script>