Why You Shouldn’t Use Vue.component
One of my favorite features of VueJS as a framework is its passive enforcement of good software development practices.
By design it does not enforce any coding style or architecture, but it makes it so easy to write things correctly, you might as well just do so.
One example is the scoped
attribute offered by the vue-loader.
Writing CSS that only applies to a single component is usually a manual process. You can wrap your component in class, or use a naming convention like Atomic Design, BEM or SMACSS. It’s not incorrect to do so, but it is manual. scoped
does all this hard work for you, in a single keyword.
Components are another example. Most front-end engineers (and libraries) agree that components are how we should be building UIs. A VueJS app, at the end of the day, is literally just a collection of components.
So, you’re probably asking: why shouldn’t you use Vue.component?
Well…
Components using Vue.component are global
It’s always been about the globals. Since the dawn of time, we’ve been fighting globals.
What a global component means is — if Component A wants to use Component B, and Component B is global, Component B can just be dropped in Component A’s template, and it will render. There is nowhere else that Component A’s requirement on Component B is listed other than the code.
This is not optimal for a variety of reasons, including:
Globals Make It Harder To Refactor Code
Module bundlers are very popular nowadays. This is good. Modules make it easier to organize our code into separate files. This makes our code not only more DRY, but easier to refactor.
How? Well…
All our dependencies are explicit. For example, if module a were to be deleted, and module b is dependent on it, module b is now broken, and the bundler will tell you this.
This is an important feature in projects with more than one person working on them because it lowers the cognitive load of working on one module.
If the file requires a global variable, how do you know what it is? An object? A function? A class? There’s no way of knowing unless it’s commented, and even then, it’s probably incorrect. And even if it was correct at some point, it may have been overwritten, because Javascript let’s you do that to globals.
Using Vue.component is the same thing as using a global variable, and comes with the same problems.
<component-name>
can be anything: a list, a dialog, a text box, a picture of a cat. Using Vue.component, the only way to find out what <component-name>
actually looks like is to use your text editor’s Find All
and search for ComponentName
or component-name
(see my point).
Naming Collisions
There is a risk with Vue.component that the name requested is already taken. This will throw an error that can only be resolved by renaming the component.
In theory, you should be able to change the name of a component to whatever you wish based on where it is. This is possible if the component is defined as an explicit dependency, which I outline how to do below (“What To Use Instead” section).
Breaks Tooling
Tooling is an important part of the equation. For Vue users who use VSCode, Vetur offers amazing support for .vue
components.
One feature of Vetur is auto-complete for component props, provided the component is locally registered, not global. If the component is global, you get… nothing.
This is important because it acts as another layer of documentation for your components. If you come from a .NET background, you know how convenient it is to get a description of what is expected in your IDE.
For Vue components, you can get the same experience. All you need to do is add a note to the component’s definition:
then it will propagate in the autocomplete for that prop.
What To Use Instead Of Vue.component
I love technical articles with paragraphs of problems, and a sentence of solution.
It’s so easy. You can avoid all the issues outlined above by only using local registration for all your components.
This means instead of this:
// ComponentName.vue
export default Vue.component("ComponentName", {...})
do this:
// ComponentName.vue
export default {...}
So whenever you need to use ComponentName.vue
, you do this:
// OtherComponentName.vue
import ComponentName from "./ComponentName.vue"export default {
components: {
ComponentName
}
}
And now literally all of the problems with global components are solved.