When working with VueJS, I feel productive and feel like my code is explicit about what it is doing. It works nicely with current Web Standards and also lets you bring your own tooling if needed.
In comparison with Angular, Vue gives the developer total freedom on how to architect the application frontend, only being concerned about rendering components on screen (the view, or le vue). There's no such thing as Modules, Services, no RxJS or anything like Angular provides by default.
This freedom simplifies the implementation but comes with a cost. If your project is heavy on the logic side of backend or has multiple components, it is necessary to expend some time to keep the house clean, organize everything and define standards to be used with the team.
It may be a little bit confusing for newcomers to know how to organize your implementation and over time I found myself taking some notes about this, to be able to improve how Vue systems are built.
Here, I want to share some of my insights, gathered from the experience of working with many distinct VueJS systems, some started by me, some started by others, and also from the experiences shared by coleagues.
Vuex is the default Global State management solution in Vue. It is part of the study path of any Vue training. It is fair to say that Vuex was inspired in Redux.
However, it is easy to misunderstand when to really use it. I've seen projects that used Vuex in every single API call, even when that specific store was used by a single component.
I've seen developers hating Vue mostly because every single API call would yield way more lines of code that needed. That's not Vue fault, but Vuex misusage.
Some may argue that "I will create it because whenever you create another component that uses it, the state is already there to be used". Well, it is true. However, you cannot predict the future to be really sure that the added complexity of Vuex will pay-off.
I believe that it is better to supose that YAGNI (you ain't gonna need it). Let the state sit in the component and just move it to Vuex if you really think this is necessary. Prefer props and events to comunicate between components.
If your team comes from an Angular background or you're reusing parts of a previous system implemented in Angular, you may also ditch Vuex entirely and give a chance to vue-rx, which allows to use subscriptions as a property of a Vue Component.
There are two concepts that come in handy to organize the Vue code.
First is the Pure Component. The Pure Component is a component that doesn't have any external API calls (using axios, a service or whatever) or directly access the global state (the Vuex stores, possibly). This is
an idea stolen slightly inspired in Pure Functions, from Functional Programming.
For instance, suppose that you have a component that renders a table of data with a single text input on top that filters the displayed content.
Also, suppose that your system have many pages, displaying different domain entities in each page, but using that same table component with different configurations, like column headers.
If you use a component library like Vuetify, you may use its v-datatable component to do the job. But, at some point you'll probably realize that most code to setup the datable is being repeated across multiple pages.
At this point it is a good idea to create a Pure Component wrapping the v-datable, let's call it, for example, FilterableTable. Now, all your shared code lies in a single place and you can use the FilterableTable in the pages without repeating all the required configuration.
The FilteredTable component does not fetch any data and must receive data to be displayed somehow. From this, we notice that we'll need a parent component passing data to the props of FilteredTable and listening to its events.
This parent component is a High-Order Component. It is a container of Pure Components. This is the right place to fetch data from APIs or consume services and to access and mutate the global state.
A High-Order Component may have multiple Pure Components and orchestrate them if necessary.
This separation between Pure Components and High-Order Components allows developers to keep components small and makes it easier to understand and maintain.
Props in Vue can be typed and validated. This is extremely useful, specially if the project do not uses typescript, for creating Pure Components and sparing some lines of test code. Trust Vue to do the validation for you.
Props do give you the possiblity to pass a whole object into a Vue component. This is something that should be done with caution.
Do not assume that the deep properties are available when your component is created and rendered unless you ensure that manually, otherwise you will face some errors and broken page renderings.
Vue will try to reach properties that still doesn't exists when creating the component and throw an error.
One useful pattern to solve this problem without needing to validate every single object property used by your component is to ensure that the component is only created when data exists or after loading with v-if.
If all the component needs is a small subset of object data, a couple to a few properties, prefer passing these as single props instead of the whole object. This will give you the advantage of validating each property individually.
The number of rows in a component may be used as an indication that the component could use some refactoring and may be simplified.
There's no fixed value for that but as a rule of thumb, I would say that a component with 100 lines is ok, 300 lines is ok-ish, 600 lines is a yellow light and 1000 has a considerable probability of implementing multiple concerns that should be separated.
There are some strategies that may be useful to refactor a component into smaller parts.
First, start by analysing your methods. Sometimes you may have a single method that just changes the value of a state variable (those in data object). In this case, you may replace the method with the attribution itself in the places where the method is called.
Methods in Vue Components usually are not meant to be used as methods in default object oriented scenarios. The main difference is that Vue Methods are usually used inside the component itself to do a single internal operation and not as a gateway to the external world which would be the case that would justify a method with a single line in object orientation.
Thus, the use case for methods in Vue is closer to private methods in object oriented languages and if this detail goes unoticed, it may lead to unecessary creation of methods.
If a component needs to comunicate with the external world, then the proper way to do it is using props to receive data and events to output data, considering a pure component.
Specifically if you have a component with let's say, 1000 lines, it certainly could be refactored into a High-Order Component plus a few Pure Components and Mixins.
For last but not least, about mixins I have the same advice from Vuex: Use carefully.
Even seeming like a great solution for code reuse, they may create unwanted coupling between components if misused, leading to maintainability issues.
Definetely, avoid having multiple mixins in the same component to avoid name colisions.
A interesting use case for Mixins is to keep pre-made event handler methods that will be used in a High-Order Component.
If you find these insights useful or want to share some more, feel free to leave a comment or reach me out on twitter at varlenneto.
PS: A big shout out for all people that took their first look on Vue influenced by me. You folks were the inspiration for this post