What is the best way to structure a Vue.js application so that it can scale to meet all your users' needs? There are multiple patterns established by the Vue community and the programming community at large that can be adopted in order to make your codebases more predictable, maintainable, and extendable.
Patterns for Large Scale Vue.js Applications
From:

Vue.js London 2023
Transcription
Hello, my name is Daniel Kelly and I am the lead instructor at the school. I am so very excited to be with you all here today. I have been a full stack developer for the past 10 plus years, worked with technologies like Wearable on the back end, and of course, vue.js on the front end. But that's enough about me. Just wanted to quickly let you know who it is you're dealing with. But now let's get on to today's topic at hand. And that is looking at some patterns for large scale vue.js app development. So our goal during this session is to answer this question. What is the best way to structure a vue.js application so that it can continue to be maintainable and extendable even as it gets to a very large size? Right? Why is this question important? Because we've probably all been given a task for a particular app before and had this exact feeling. We just don't know where to begin. We don't even know how to approach the issue, maybe because things are a little bit messy. So our goal is to eliminate this feeling here as much as humanly possible. All right? We all want to enjoy our jobs and be less frustrated. Okay, so I'm going to start with this key tip for making a large scale application. That is, make your code base predictable. This first tip is really not that exciting. It's really not that flashy. But to be honest, this is one of the keys, in my opinion, to building a maintainable app. Make it as predictable as possible by following standards so that everybody knows where to look and how certain things work. Right? So, this begs the question, where do standards for the vue.js community exist? All right? Well, place number one that comes to mind for me is the vue.js style guide. This is the official place for styling standards for the vue framework. Here is an example of some standards designated within the document, okay? When it comes to naming your components. First of all, it says that all of your components should be in the Pascal case. It also mentions you should prefix your base components with the word app or base. So these are all those components that you're going to be using throughout your application. And they're a lot more general use case, right? These are things like app button, app table, app modal, things like that. Things that accept props down and events up and probably don't rely on any global state. Okay? I actually prefer to use app in this instance because it always puts my base components at the very top of my components directory. It also mentions that you should have multi-worded names for your components. This is actually even more than just a style thing. It's also to prevent conflicts between existing or future html elements and your vue components. Okay? Another standard is to prefix all your single instance components with the word the. Okay? These are any components that are just going to be used on the page a single time. Now they could absolutely be used on multiple pages, but they only appear on a single instance of a page at a time. Okay? So this is usually layout type components, things like the header, the footer, the sidebar, so on and so forth. It's also mentioned that you should prefix tightly coupled child components. For example, if you had a component for an item that always existed within a to-do list, you would prefix that component name with to-do list and then item. Okay? A real world application of this that I used in a code base at my previous job was I had a job form component, and then there was a special field for choosing the location where a job should be advertised called job form location map field. Okay? So this tells any other developers within the project that this component is really only meant to ever be used in the context of the job form. And they can get that just by looking at the component name. Okay? This is great. And there are some other tips on styling your component names within the style guide as well, but this is just a good sampling, and there's certainly a lot more available in the style guide besides just component naming, but this is just a good sampling to kind of show you, hey, if you implement these standards, it's one less cognitive thing that people who are onboarded to your project have to think about because they're standards that everybody already knows. Okay? By the way, just at the time of this talk, it is important to note this warning at the top of the style guide, and that is it's currently a bit outdated. Most of the examples are in the options api only, and there are no rules regarding script setup and the composition api. However, as you just saw, a lot of the things for styling don't even necessarily have to deal with one api or another. All of the component naming things could apply to either api. So this is still a great resource, but do expect it to be updated in the future. Okay? Another really great resource for standards is the officially recommended tooling for vue.js. All right? That's found on the tooling page of the official vue.js docs. It provides reference to things like vite for your development server and Apenia for global state management. This might seem like it could go without saying, but maybe I've ignored some of my own advice in the past, and I want to make sure you don't do the same. What do I mean? Well, one of the projects I was working on prior to joining vue school, of course, needed global state management. Well, I wasn't thrilled about the DX that vuex provided, and so I came up with my own global state management solution based around the vue.observable api in vue 2. Long story short is, yes, there were some DX improvements. However, there were more downsides than upsides. There were caching issues that I didn't handle that vuex would have already handled for me under the hood. There were onboarding issues when it came to moving new people to the project who I had to explain this custom solution to that if I had just used vuex, they would have already been familiar with it, or at least I could have just pointed them in the direction of the vuex docs. Right? So the key takeaway here is if there is an officially recommended solution for your problem, definitely think long and hard before you use anything else. Okay? So predictability from the style guide, predictability from the officially recommended tooling, and then finally, another great source, I think, of predictability is the meta framework NUX3. It makes a lot of opinionated decisions, and it's such a huge player in the vue.js community, a lot of people are going to be familiar with the conventions that it provides. So definitely recommend taking cues from NUX3 when it comes to structuring your application, or just using NUX3 to begin with. By the way, you will see NUX3 come up a few more times in this talk because I think it's such an excellent piece of software. All right. So tip number two is to take full advantage of your IDE. This is, after all, the software that you're going to be using most in order to develop your application. Number one, that means make sure to install Volar. That's probably pretty obvious. However, number two is also make sure you have ESLint installed on all your projects. I've had projects that I've worked on before where we didn't do this, and let me tell you, it was an absolute pain because ESLint is going to help you catch items directly inside of your IDE without having to wait to find out about them at runtime. In fact, it's going to show you errors in your IDE that you might not even get hard errors for within the browser. So for example, in this little piece of code, we're using a V4 to loop over item in items, but we have left off the key. This would not error in the browser, but my IDE knows now because of ESLint, I should really add this key to my V4. And then the same thing down here for this hello world component. It's being imported and registered. However, it's not actually being used anywhere inside of my template. And so ESLint is saving my end user from having to download this extra code that's never even used. Right? I get this essentially for free just by installing ESLint. It is an absolute no brainer. Similar to this is making sure your IDE's formatting is setting up as set up appropriately. This could be the formatting that is built in with ESLint, or it could be a more opinionated formatter like Prettier. But no matter what, make sure your IDE is set up to do this formatting automatically for you. Be that on file save or whatever. Just make sure you aren't having to take that cognitive load yourself. It's just not necessary, the tooling can do it for you. If you'd like some more tips and tricks about taking full advantage of your IDE in the context of a vue.js project, I highly recommend you check out this highly rated course that we have at vue School. A lot of people have given me really great feedback that this has helped them optimize their workflow. It's called Visual Studio Code for vue.js Developers. And in it we go over some of the things I already just talked about, but we also get into things like utilizing vue.js snippets that are provided by the community, creating your own vue.js snippets, optimizing your workflow for Git in terms of using your IDE, and a whole lot more. So definitely check that out if Visual Studio Code is your IDE of choice. Lastly, I really, really, really recommend that you consider using typescript in your large scale vue.js projects. They are going to take your IDE's knowledge of the code to an absolute other level that's just not possible without typescript. This is going to help you prevent errors as you're developing your code, it's going to make your refactors less stressful and less risky, and it's going to help give your autocomplete superpowers. If you aren't familiar with using typescript in the context of a vue.js project, at vue School, yes, we also have a course for you there as well. We'll teach you how to type component prompts, type component events, type template refs, and a whole lot more. And even if you're brand new to typescript and don't know just the basics, we have a typescript fundamentals course for you as well. I just don't have the time to get into all the specifics of those things during this talk, but I want to give you the resources. Finally, NUX 3, note, does have typescript installed by default. And if you're brand new to typescript, I really think using NUX 3 is a great way to dabble and just play and get used to using typescript in a new project. Why? Because it's just installed by default. You can use javascript all you want. You can use regular javascript components. And then just as you see necessary, implement a typescript component here and there and sprinkle it in your project progressively as you get used to it. Okay, really great way to get started with typescript. And by the way, in the context of a NUX project, you even get some special types generated for you on the fly by the framework. So for instance, if you've got some api endpoints set up internally within the project based on a special file convention, the NUX specifies, you're able to get auto-complete whenever you call fetch on any of those api endpoints because it generates these types on the fly based on the conventions of the file structure. So that's really cool. Speaking of file structure, I always get this question, how should I organize my files for my large scale project? And my simple answer nowadays is just use the standards that NUX provides. And in fact, just go ahead and use the standards that are actually inside of the project. Actually start your project with NUX 3 because not only do you get the great standards, but you get some great magic on top of those standards. For instance, any components that are created within the components directory are auto-imported for you. You don't have to manually import those throughout your project. Same thing goes for composables. Plugins are also auto-registered and pages inside of the pages directory are automatically turned into routes. This is called file-based routing. So a lot of goodies there when it comes to NUX's file structure. Great conventions, but also great functionality. Tip number four is come out with a naming convention for your routes. This is not something that's specified efficiently for vue.js anywhere. However, a lot of other frameworks out there do have route naming conventions. The one you see on the screen in front of you now is pulled directly from the Laravel docs. So I could be working on a vue.js front end and other developers who are on my team who are familiar with Laravel could also be working on this front end and they could have a predictable way of working with the routes if I follow this same convention for my front end. So your team might not use Laravel. That's perfectly okay. My point is have some kind of convention in place. If you need inspiration for one, I definitely suggest you check out the Laravel docs because this convention is a really great one. Tip number five is wrap your third-party code. This is a concept that Josh Deltner explains very well in his article entitled NUX enterprise patterns Provider Abstractions. Why should we do this? Well, it decreases our chance of third-party lock-in and increases the surface area for app-specific optimizations. You might not actually understand what that means, so let me show you with a little example code. All right. So let's pretend we're using Axios in our project in order to make our AJAX requests. Now Axios is popular enough that I wouldn't actually suggest wrapping Axios. I would suggest using it directly. However, it's a good and common library that I think helps people understand this pattern. So I wouldn't actually wrap this in your apps, but I would do it for similar third-party dependencies. Okay. So let's say you have Axios. In order to wrap Axios, you could create a class called HTTP and then define a get method on that class. Under the hood, all you're really doing is calling the Axios get method. And then you could do the same thing for the other method types and so on and so forth. But why in the world would I want to do this, Daniel? I mean, I'm not getting paid to write per line, right? Well, the advantage here is that if for some reason we ever had to replace that dependency with something else, I could very easily just do that in one single place. So here we've changed out Axios for the browser native fetch function. And the beauty is we've only had to change it out in one place, that is in our HTTP class. We didn't have to search throughout the code base, do a bunch of messy finds and replaces. We only had to focus on this single file. And the other beauty about this is that all the other developers working on the project who are using this HTTP class throughout the project, they don't even have to know that I've updated the dependency. They just continue using the HTTP class as usual. So this is a really great solution when it comes to dependencies that you're maybe not so sure about, that you think might not have the longevity, you might be replaced later on with something else. So I'm not saying wrap all of your third party code, but be choosy and do wrap some of your third party code. Another great benefit of wrapping this third party code is that it surfaces opportunities for extending the functionality for app specific use cases. So for instance, let's say I wanted to handle all of my HTTP errors a particular way in my app. Well, at that point, it's as easy as adding a try catch within my single class. Now, of course, this user experience isn't great, but you get the idea. I can have these app specific optimizations all wrapped up in my class. And then when I go to use the class throughout the application, I don't have to worry about reporting these errors to the user or whatnot. Same kind of thing could be applied for a caching mechanism. All right, I think you get the idea. By the way, this also works for components and not just for functional helper libraries. So for instance, this app icon component I have here is actually made to make my icon solution for my app generic and even extend it so that I can use two different icon solutions at once. So you see here my app icon wraps font awesome as well as material design icons. And I provided a single interface that is the app icon component for actually implementing the icons throughout my application. Awesome. All right, so tip number six is to interact with your back ends via an SDK. Fine, well, take a look at these two lines here. Which one of these would you rather write? Would you rather write the full request out something like this? Or would you rather write something short and simple like this? For me, the answer is clear. You want to do option number two, right? Well, there's several different advantages if you do choose this option. Number one, and most simplistically, it helps you prevent typos. Okay, you're not going to have to worry about typos in the path of your api endpoint. But it also provides you the opportunity to do client side data normalization in a single place. Think turning all of your date strings coming from the back end to actual date objects in javascript or any other derived data, perhaps like computed things you need to do or something like that. Okay, that can be wrapped up in the actual SDK. And your actual client code doesn't have to think about it. Okay. It's also easier with SDKs to spot errors and provide type safety. By the way, you don't have to write these SDKs for yourself. A lot of solutions out there come with javascript SDKs already built out for you. So things like Firebase are really great for this, which by the way, we use in the vue.js 3 masterclass as a back end. And Superbase, which is a self-proclaimed open source alternative to Firebase, is a solution I really like, where they actually even provide a command for automatically generating your typescript types based on the structure of your database. And this makes it really, really great to work with. Okay. By the way, if you're into Laravel, there's also this Laravel query library, query builder library, that does much the same as some of these other two that I just talked about. So I'd suggest checking that out if you're into Laravel. Awesome. That is my time with you today. I wanted to leave you with just a few recommendations for some vue school courses that I think will help you build better large scale apps. Number one is the typescript with vue.js 3 course. We've already talked about a little bit. Next is our Pina, the enjoyable vue store course. This is going to help you get a great grasp on the official Pina state management solution for vue. And lastly is wrapping rapid testing with V-test. All right. By the way, we also just finished releasing this great new course called AI Chatbot with vue.js and GPT-4. If you're interested in implementing AI into your applications, your vue.js apps, I definitely suggest you check this out. And the official vue.js certification has launched and pre-orders are being accepted. So this is something that the vue school team is doing in collaboration with the core vue team. If you'd like to prove your vue.js development skills to potential employers or potential clients, this is a great way of doing that. Thank you all very, very much for your time. And I hope you have gained some valuable knowledge from today's talk. You all have a wonderful day and keep coding. Thanks.