Header image above is a screenshot from the official website of Vue 3.
Vue 3 is now stable since June 8, 2021 with version 3.1.0 named Pluto. It is Typescript compatible and introduces Composition API where you can initialize most parts of the component inside the setup
function. If you want more info about Vue 3, you can check the links below:
- Vue 3 Official Website: https://v3.vuejs.org/
- Vue 3 Github: https://github.com/vuejs/vue-next
- Stable release note: https://github.com/vuejs/vue-next/releases/tag/v3.1.0
- Composition API: https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api
Let's immediately proceed with the topic of this article. Vue 3 already has a documentation about registering components globally. Global components are Vue components that can be declared immediately inside the template of another component without import and declaration in components property. If you're not familiar with Vue 3, this is how we import other components inside a component:
<template>
<div>
<SomeComponent/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import SomeComponent from '@/components/SomeComponent.vue';
export default defineComponent({
components: { SomeComponent },
});
</script>
Assuming the file above is inside the views
directory and we're importing a reusable component from the components
directory. As you can see that it's not different on how we import components in Vue 2 except the difference in creating a component and the usage of Typescript (by adding lang="ts"
inside the script tag).
If we make the component globally accessible to other components, this will be very helpful when our project becomes big making it more reusable. It will also remove the import of multiple components inside a component unless the component is a local sub-component. See example below:
<template>
<div>
<SomeComponent/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
});
</script>
As you can see we just declared the SomeComponent
component like a HTML element directly inside of another component without the import
syntax and the components
parameter inside the defineComponent
function.
So how do we make a global component in Vue 3? According to the official documentation, here's how we register a component globally in main.ts
file if you're developing using Typescript else in main.js
file:
import { createApp } from 'vue';
import ComponentC from './components/ComponentC.vue';
// Usually it is App.vue inside the createApp function.
// App.vue is your main or root Vue component.
const app = createApp({});
// Component named with kebab-case.
app.component('component-a', {
/* ... */
});
// Component named with PascalCase.
app.component('ComponentB', {
/* ... */
});
app.component('ComponentC', ComponentC);
// The second argument of the component function is the object that creates a component or it can be an imported component instance.
// It willl mount the Vue app inside the HTML element with ID of
// #app in index.html which is located in public directory.
app.mount('#app');
Reference: Vue 3 Global Registration
If we follow the procedure above, it will bloat our main.js
or main.ts
file especially if our application becomes bigger then our reusable components will increase. So how do we prevent that from happening?
I actually got the idea from Vue 2 documentation on registering global components dynamically which I always use when developing a frontend application using Vue 2. In Vue 3, they don't have this section in their documentation.
Reference: Automatic Global Registration of Base Components
Here's my implementation:
./components/base/index.ts
import { App } from 'vue';
const requireComponent = require.context(
// The relative path of the components folder
'./',
// Whether or not to look in subfolders
false,
// The regular expression used to match base component filenames
/[A-Z]\w+Base\.vue$/,
);
const register = (app: App<Element>): void => {
requireComponent.keys().forEach((fileName) => {
// Get component config
const componentConfig = requireComponent(fileName);
// Get component name
const componentName = fileName.split('/').pop()?.replace(/\.\w+$/, '') as string;
app.component(componentName, componentConfig.default || componentConfig);
});
};
export default {
register,
};
If you checked the link I gave above for Vue 2 implementation, it is almost the same as my implementation above for Vue 3. Here are the differences in my implementation:
- I have an argument of
app
with type ofApp<Element>
.App
type is fromvue
package and theElement
type is more of an interface. See more details here. - My relative path is
'./'
because I placed this code in./components/base/index.ts
. - My regex will get all the files that ends in
Base.vue
for them to be considered as a global component. I prefer to place all global components inside thebase
directory but you can name it anything you prefer.- e.g. GlobalComponentBase.vue
- I didn't used the
lodash
package if you prefer minimum external packages.
Here's how I implemented it in the main.ts
file:
main.ts
import { createApp } from 'vue';
import BaseComponents from './components/base';
const app = createApp(App);
BaseComponents.register(app);
app.mount('#app');
That's it! Your main file will not bloat when you have hundreds of global components to register. I hope this article will help you a lot. 😉
I also created a Github Gist of the code implementation above:
https://gist.github.com/jirehnimes/2fcb31a2cbe7bb0c722a96f49e4cbf8f
Cheers!
Top comments (4)
Hi Jireh, thanks for this interesting post.
Maybe you have an issue when import App from vue, I solve it using getCurrentInstance and using appContext.app.component to register my components.
I'm wondering, how I can unregister my components? Should I delete from components array?
Thanks!
Hi, sorry for the very late response.
When I did this article, I never experienced any issue/s during that time though thanks very much for sharing your solution.
Are you planning to unregister the components but the files still remains in the project? I think the best way is to have an array that will exclude the components during the loop inside the
register
function.Thanks a million for this! I've got a pretty hare-brained idea that would massively benefit from this workflow in terms of cleanliness and organization. I'm trying to generate Vue components on a server based on parameters received from my Vue frontend, then dynamically register those components after they are fetched from the server and saved in the base components directory. That last bit about saving the fetched component to a local directory is the part I'm still kinda scratching my head about...
Hi, sorry for very late response on this. Are you talking about SSR?
Actually, this article will not work on the updated version of Vue 3 because of
require.context
. I just tried a modified version working in Vue 3 + Vite both for SPA and SSR that usesimport.meta.glob
instead. I'll create an another article for it.