Auto-registering all your components in Vue 3 with Vite
Why auto-register components?
I'm actually a big fan of manually importing components in Vue applications. It makes it very clear where every component comes from, doesn't rely on ✨magic✨, and most IDEs can do auto-imports for you anyway so it's not much more work for you.
That said, in an environment where I'm not building an SPA, and I'm using Vue as a progressive enhancement tool, I want all of my components to be available in HTML. To make this happen, I have to register all of them in the root Vue instance....
import { createApp } from 'vue'
// import each component
import Fruits from './components/Fruits.vue'
import Vegetables from './components/Vegetables.vue'
const vueApp = createApp({
// register each component
components: { Fruits, Vegetables }
})
This process is tedious, and makes component auto-registration totally worth it IMO.
How
So, to auto-register our components, we need to do a few things:
- Get a list of each component
- Import that component
- Register it on our Vue instance
Luckily, Vite has an amazing feature which takes care of steps #1 and #2 for us
Step 1+2: Glob Imports.
Glob Imports is feature of Vite that allows us to import multiple files based on a filepath.
There are two ways to use Glob Imports in Vite: lazy or eager. If you use the standard glob
method, the imports will be processed as dynamic imports, so the components will be lazy-loaded. In our case, we want to import all of the components directly into our main bundle, so we'll use the globEager
method.
Note: Glob Imports is a Vite feature, and is not part of any JS or "platform" standards.
Here's how Glob Imports works:
// import multiple components
const components = import.meta.globEager('./components')
And here's the result of that import:
// code produced by vite
// magically autogenerated module imports
import * as __glob__0_0 from './components/Fruits.vue'
import * as __glob__0_1 from './components/Vegetables.js'
// our components variable now contains an object with key/values
// representing each module's path and definition
const components = {
'./components/Fruits.vue': __glob__0_0,
'./components/Vegetables.vue': __glob__0_1
}
Step 3: Registering components
Now that we've actually imported each component, and we have a list containing the path and definition, we need to define these components on our Vue instance.
To do that, we'll loop over each entry in our components
object, figure out the component's name based on the file name, and then register the component on our Vue instance.
Object.entries(components).forEach(([path, definition]) => {
// Get name of component, based on filename
// "./components/Fruits.vue" will become "Fruits"
const componentName = path.split('/').pop().replace(/\.\w+$/, '')
// Register component on this Vue instance
vueApp.component(componentName, definition.default)
})
Putting it all together
import { createApp } from 'vue'
const vueApp = createApp()
const components = import.meta.globEager('./components/*.vue')
Object.entries(components).forEach(([path, definition]) => {
// Get name of component, based on filename
// "./components/Fruits.vue" will become "Fruits"
const componentName = path.split('/').pop().replace(/\.\w+$/, '')
// Register component on this Vue instance
vueApp.component(componentName, definition.default)
})