DEV Community

Cover image for How to create a scalable vue.js setup (Part I)
Marcel Cremer
Marcel Cremer

Posted on • Edited on • Originally published at marcel-cremer.de

How to create a scalable vue.js setup (Part I)

If you know me a little bit or read my bio in social media, you'll probably have noticed that I'm a big fan of vue.js (and if you don't follow me right now, consider following me on twitter šŸ˜œ).

This is for a reason: Coming from angular, I personally like the approaches they chose to organize and do things. They have dependency injection, routers, stores, a test harness and much more. However, it has also a big downside: Everything feels a bit big and bloaty and I always had the feeling, that I could not develop as fast in angular as I should.

Introducing my vue.js setup

If you don't know vue.js, you can check it out here. Vue.js is a rather small framework (20KB compressed and minified) that focuses on the "view" part of the application (that's why it's pronounced /vjuĖ/, like "view" - just in case you wondered). Additionally, there are plenty of resources, plugins and so on that you can use to customize it to your needs (a comprehensive list is available here: awesome-vue). So let's start:

The Foundation: Vue-cli

Comparable to angular, the vue environment also has a cli that you can use to generate an application according to your needs. Even better, you can choose what tools you like to use and you can configure it to your needs (e.g. webpack) without ejecting the configuration. A simple command

vue create <projectname>
Enter fullscreen mode Exit fullscreen mode

creates a new project and you are able to choose, which css-framework you like to use, if you want babel and / or typescript, if you create a PWA (automatic manifest generation), which test framework for unit and e2e tests you prefer and if you opt for eslint or the deprecated (but still awesome) tslint. This alone is a huge advantage, as you are able to create the environment in a way that perfectly matches your needs. I personally use the configuration

[Vue 2] dart-sass, babel, typescript, pwa, router, vuex, unit-jest, e2e-cypress
Enter fullscreen mode Exit fullscreen mode

but this can be very opinionated. After creating the project, you can launch a pretty cool dashboard via

vue ui
Enter fullscreen mode Exit fullscreen mode

which shows you not only your projects (it's possible to have a multi-project Monorepo in vue), but also the bundle sizes and the transpilation statistics. It's awesome!

Getting stuff: Dependency injection

As mentioned before, vue only concentrates on the view part of your application. So the next thing I usually tend to do is to introduce dependency injection and for this task, I absolutely love inversify.js although people are discussing on how to proceed with future maintenance. It's very well established and plenty of larger projects are using this, so that I'm optimistic that even if the maintainer does not support it in the future, there will be forks or other ideas to further support this framework.

After installation, I basically do the whole DI configuration in three files:

  1. di.types.ts
export const TYPES = {
  /**
   * Services
   */
  DATA_SERVICE: Symbol.for('DATA_SERVICE'),
}
Enter fullscreen mode Exit fullscreen mode

This file defines Symbols, so that inversify has unique tokens to inject things.

  1. di.container.ts
import { Container } from 'inversify';
import getDecorators from 'inversify-inject-decorators';

export const container = new Container({ defaultScope: 'Singleton' });

const { lazyInject } = getDecorators(container);
export default lazyInject;
Enter fullscreen mode Exit fullscreen mode

This file is just to create a singleton container and make it possible to "lazy inject things". This is necessary due to a babel / webpack issue: On startup of the application, several files can request stuff and if you don't have the container imported from an own module, some things like lazy inject won't work

  1. di.config.ts
import 'reflect-metadata';
import { container } from '@/di.container';
import { TYPES } from '@/di.types';
import { DataService } from @/services/data.service.ts;

container.bind<DataService>(TYPES.DATA_SERVICE).to(DataService);
Enter fullscreen mode Exit fullscreen mode

Here we bind a service to our previously defined Symbols. That's basically the whole magic about itroducing dependency injection. Easy, wasn't it?

Property decorators

The next thing to tackle for a scalable application is, that we want to have a well written component that is easy to read and to reuse. Vue components are usually single file, which has it's benefits (e.g. if you want to change some css, and then html and oh, you also need to introduce this property in typescript...). It's also easier to maintain some code standard about how large a component should possibly become, if you had single file components (e.g. >250loc => linter error). To additionally increase readability, I like to use typescript class components flavoured with vue property decorators.

Here's some example, how a class component with decorators and dependency injection could look like:

<template>
    <div class="hello-world">
        {{ message }}
        The sum of {{ a }} + {{ b }} is {{ sum }}.
        Our current mood is {{ mood }}
    </div>
</template>

<script lang="ts">
    import lazyInject from '@/di.decorator';
    import { TYPES } from '@/di.types';
    import { Component, Prop, Vue } from 'vue-property-decorator';

    @Component
    export default class HelloWorld extends Vue {
        mood!: string;

        @lazyInject(TYPES.DATA_SERVICE) 
        dataService!: DataService;

        @Prop()
        message!: string;

        @Prop()
        a!: number;

        @Prop()
        b!: number;

        get sum() {
            return a + b;
        }

        async mounted() {
            this.mood = dataService.get(`/api/v1/mood`);
        }
    }
</script>

<style lang="scss" scoped>
.hello-world {
    color: blue;
}
</style>
Enter fullscreen mode Exit fullscreen mode

pretty neat, isn't it? šŸ˜ Having this result is a good first step for creating a scalable vue app. If you are interested in more of this topic, I'm gonna talk about state management, testability and deployment (e.g. different environments) in the next part of this article series. Thank you for reading!

Top comments (4)

Collapse
 
vonziu profile image
Konrad Stącel

I have some concerns about decorators. They are not valid javascript, and won't be implemented anytime soon into language. Isn't it better to avoid it? (I guess TS will still support it because a lot of projects e.g Angular are based on it but still, it might be a pain in the ass to refactor a fully-fledged project back into valid javascript, or get rid of decorators, if TS decide to not support them anymore).

Collapse
 
marcel_cremer profile image
Marcel Cremer

Hey there,

as you described, decorators are currently in proposal state for JavaScript (github.com/tc39/proposal-decorators) and experimental for typescript (typescriptlang.org/docs/handbook/d...). However, they are widely spread (like you mentioned: angular, react, mobx, ... - there are plenty of famous examples), so I personally think it's no question if, but only when they will arrive in ECMA standard.

Of course this is highly opinionated, but as they are so commonly used nowadays, I don't have bad feelings about using themšŸ˜‰

Also, depending on your configuration, it's possible that your transpiler (e.g. babel) transforms it on compile-time into standard javascript.

Cheers

Collapse
 
mdhesari profile image
Mohammad Fazel

Very well written.

Collapse
 
marcel_cremer profile image
Marcel Cremer

Thank you very much :)