[Vue3] How to Encapsulate an Imperative Component
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:
Loading...
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
- Dynamically create a component instance: Use
createAppto create an instance of the FullscreenLoading component. - Mount to the DOM: Create a div container, mount the component instance to it, and append the container to body.
- Close function: Return a
closefunction 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!