log
Swift Code Chronicles

[Vue3] How to Encapsulate an Imperative Component

Published on December 9, 2024
Updated on January 6, 2025
19 min read
Vue

In development, we often need global loading components (such as a full-screen loading indicator) to display operation statuses. For instance, here is a simpleFullscreenLoading.vuecomponent:



Generally, we can use the component in the page template and control its visibility with a variable:

<template>
  <FullscreenLoading v-if="showLoading" />
  <div class="wrapper">
    <button @click="clickHandler">Show Loading</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import FullscreenLoading from './components/FullscreenLoading.vue'

const showLoading = ref(false)

const clickHandler = () => {
  showLoading.value = true
  setTimeout(() => {
    showLoading.value = false
  }, 2000)
}
</script>

While this approach is straightforward, it introduces repetitive code if multiple pages require the same loading logic. You have to manually import the component and manage its state for each page.

To address this, we can encapsulate the loading logic into an imperative component, enabling us to show it with a single function call without additional imports or state management.


Encapsulating an Imperative Component

Using Vue’s createApp method, we can dynamically create a component instance and mount it to the DOM:

import FullscreenLoading from '@/components/FullscreenLoading.vue'
import { createApp } from 'vue'

export function showLoading() {
  const app = createApp(FullscreenLoading)
  const container = document.createElement('div')

  app.mount(container)
  document.body.appendChild(container)

  return {
    close: () => {
      app.unmount()
      document.body.removeChild(container)
    },
  }
}

Code Explanation

  1. Dynamically create a component instance: Use createApp to create an instance of the FullscreenLoading component.
  2. Mount to the DOM: Create a div container, mount the component instance to it, and append the container to body.
  3. Close function: Return a close function that unmounts the component and removes the container from the DOM to prevent memory leaks.

Using the Encapsulated Imperative Component

With the showLoading function, you can display a full-screen loading indicator simply by calling it, without importing the component or managing state:

<template>
  <div class="wrapper">
    <button @click="clickHandler">Show Loading</button>
  </div>
</template>

<script setup lang="ts">
import { showLoading } from './utils/loading.util'

const clickHandler = () => {
  const { close } = showLoading()
  
  setTimeout(() => {
    close()
  }, 2000)
}
</script>

When you call showLoading, the loading component is displayed automatically. To close it, call the close function returned by showLoading. This approach is straightforward and avoids repetitive code across pages.


Summary

By encapsulating the loading logic into an imperative component, we simplify the process of showing and hiding global loading indicators. With showLoading, you can display the loading component instantly, and close it by calling the close method. This encapsulation reduces template complexity and enhances the reusability of the component, making it ideal for dynamic UI operations in similar scenarios.

I hope this helps!

About

A personal blog sharing technical insights, experiences and thoughts

Quick Links

Contact

  • Email: hushukang_blog@proton.me
  • GitHub

© 2025 Swift Code Chronicles. All rights reserved