Learn to manage large Vue.js components in 4 min
Vue.js is, in my opinion, the nicest JS framework currently available. One of the features I like the most are the .vue
single-file components. It can get quite hard to manage larger ones tough, especially when you start creating custom sub-components and adding static assets. Here are some things I’ve tried, you might like them too.
Some quick basics
This might be common knowledge to you but just in case:
- Use PascalCase file naming.
Example:ComponentName.vue
doc reference - Preface base components with a default word like App, Base or V.
Example:AppTable.vue
doc reference - Preface components with a max of one active instance with
The
.
Example:TheLargeComponent.vue
doc reference - Always use multi-word names for your components. Prefacing
App
orThe
simplifies this but do this for all components. This is to avoid clashes with standard HTML tags which are always a single word.
Example:MainModal.vue
instead ofModal.vue
doc reference
How it is often done.
I’ve seen this all too often, and with large projects this becomes a nightmare. All components are in the components
folder, all assets are in a central assets folder and single-instance child components are only indicated by prefacing the name of it’s parent (this gets super fun after 2 layers). Often this looks a little like this:
components
|--LargeComponent.vue
|--LargeComponentModal.vue
|--LargeComponentModalTextSection.vue
|--LargeComponentModalTextSectionTitle.vue
|--LargeComponentModalButton.vue
|--LargeComponentModalButtonText.vue
assets
|--img
| |--large-component-banner.png
| |--large-component-button-bg.png
OK, so this obviously gets cluttered fast, how do we fix this? Let’s get to the first option!
1. Putting the component in it’s own folder.
One of the first options I tried. Name the folder LargeComponent
and put the original component in it. You can do two things here.
First you can name the component index.vue
which probably works in your build system and has the great benefit of not changing your import statements. This is really really handy but a very hacky and not the most stable solution. Note: this does not work if you are specifying the file extension in your imports like LargeComponent.vue
components
|--LargeComponent
| |--index.vue
| |--components
| | |--LargeComponentModal.vue
| |--assets
| | |--img
| | | |--banner.png
Secondly you could just name it after it’s parent folder and thus keep it LargeComponent.vue
which is probably the wisest choice here. This does change the import from ./components/LargeComponent
to ./components/LargeComponent/LargeComponent
which isn’t ideal.
Pro’s
- All items in the components folder indicate and represent a component.
- Easy to understand for other dev’s.
- Sub-components and assets can be imported with clean relative imports:
./assets/img/banner.png
Secondly an approach which looks very much like this one and probably the most straightforward one:
2. Putting the folder alongside the vue component.
This solution has no problem with specifying the file extension and does not change the imports.
components
|--LargeComponent.vue
|--LargeComponent
| |--components
| | |--LargeComponentModal.vue
| |--assets
| | |--img
| | | |--banner.png
The imports in the file itself do look a bit more complex but are also very clear: ./LargeComponent/assets/img/banner.png
. It also makes the main components folder look a bit more cluttered but not necessarily more unclear.
Pro’s
- Works in every build system, very straightforward.
- Easy to understand for other dev’s.
- Does not change imports when creating folder for component.
Finally there is a bit more uncommon one I really like if you are using imports without file extension specified:
3. Putting an index.json
in your component folder
Pretty straightforward, you just throw this index.json
file in your component folder:
{
"name": "MY_COMPONENT_NAME",
"private": true,
"main": "./MY_COMPONENT_NAME.vue"
}
This takes care of resolving the import correctly. You can keep a tidy structure in your components folder with every element representing a component. You’ll end up with this folder structure:
components
|--LargeComponent
| |--index.json
| |--LargeComponent.vue
| |--components
| | |--LargeComponentModal.vue
| |--assets
| | |--img
| | | |--banner.png
This has all the advantages of the methods above but it is a bit more obscure and looses one of it’s biggest advantages when using imports with file extension specified.
Pro’s
- Does not change imports when creating folder for component.
- All component data contained in one folder.
- Relative imports are cleaner.
Bonus round: Tip for Vue-Router users
When building an SPA I really like to put the single-instance components in a folder corresponding to the page they belong to, like this:
views
|--SubscribeView.vue
|--SubscribeView
| |--components
| | |--TheHeader.vue
| |--assets
| | |--header-image.png
components
|--AppButton.vue
|--AppButton
| |--components
| | |--AppButtonText.vue
| |--assets
| | |--img
| | | |--button-background.png
That’s it!
Thanks for reading, really hope this helped, if you’ve got suggestions and improvements please do leave them in the comments!
Building stuff? I love making things too, if you want to check out what I’m doing follow me on twitter!
Extra optional bonus round: More about the why!
I got a question in the comments from Kasp which I think addressed a little bit of info that might have been missing from the post. The answer got a bit long so I decided to just include the it in the post itself. The question is as follows:
Why separate components based on number of instances?
The way we’re currently doing our Vue app (which hasn’t gotten that big yet) is have a components folder and a views folder. Each view essentially being a page with one or more components. Components folder strictly contains the basic building blocks of the app.
Can you suggest when it might get bad? I’m thinking when there’s too many pages the views folder might get overwhelmed.
Well first of you probably want to separate single-instance components(SIC) from the rest. These SIC are just used to make building your page more convenient and to separate logic. I already really like to include these in a components folder coupled to the view. No other component will or should ever be using these single-instance components so there is no point in making them accessible to other views/components. Besides this there is a big advantage in making it easier for the view itself to access them and making it clearer that that is what is happening.
This starts to enable you to just use the components folder for what it sounds like it is: a folder full of reusable components that you can use anywhere in your app instead of a just a folder full of everything that ends with “.vue” and isn’t a top-level view.
But you’ll probably need assets. Certainly for your larger components. It’s quite inconvenient to place these in a general assets folder. You could just place them all in the main /static
folder but I personally think it’s pretty nice and way easier to work with to have a dedicated folder for each component.
You might already be guessing where this is going but the folder you just created to hold this assets folder is the perfect place to put the single-instance children of the component you’re creating this folder for (preferably in a… you guessed it components
folder!). Again just as with the views, no other component will or should every be using these and it should be as easy and straightforward as possible for the one component that will.
Thanks for asking this question and hope my answer clears things up!
Cheers!