Skip to content

Instantly share code, notes, and snippets.

@igogrek
Last active April 27, 2024 03:59
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igogrek/58046e1dda1638012299129bd0ae4ffd to your computer and use it in GitHub Desktop.
Save igogrek/58046e1dda1638012299129bd0ae4ffd to your computer and use it in GitHub Desktop.
How I started loving Vue

This article is the logical conclusion of my previous article - How I stopped loving Angular. Recommended to check before reading this one.

We have been using Vue instead of Angular for nearly a year now. In this article, I will highlight my impressions on Vue and differences compared to Angular.

Moreover, I will add some hands on experience notes from usage of Vue on real projects.

Short recap

Here is the recap of my previous article. Short list of main Angular problems for me:

  1. Horrible router
  2. Heavy and not-so-useful Dependency injection (see below)
  3. Very arguable module systems (not used in any other framework)
  4. Many unnecessary abstractions, strange API-design
  5. Observable as a core of the framework

...

Why not React

In addition, little note regarding why we did not take React as our framework.

Disclaimer: all points below are VERY subjective (so don't take them as React criticism but a personal point of view):

  1. Reactivity in Vue just works, out of the box and asynchronously: no need for immutability and pure functions (don't get me wrong - both these concepts are great, but usually they are quite verbose, almost always that's longer/expensive)
  2. JSX is bad for a few reasons:
    • Slow migration of existing HTML markup to the application code - especially a problem for some designers, as sometimes they have to ask developers for help with some rather trivial things
    • You always have to split everything into minimal components, even if you don't really need it. Compared with Vue / Angular templating where you only extract component at the time of need
    • It's still fresh in my memory that conditions = pain
  3. Forms are quite problematic (see below)
  4. create-react-app IMHO is less functional than any other front-end CLI for popular frameworks, React should have something much more powerful
  5. Our team had vast Angular expertise - given that Vue is similar in some aspects it was much either to get into

Differences and impressions after Angular

A little bit about main differences and impressions that I noted after coming to Vue from Angular.

Documentation

First thing that you will run into after choosing UI framework is, obviously, documentation.

I will not repeat myself about Angular's docs with those

Banana in a box

But I will note that Vue docs are much more simple and straightforward.

Also, I will not provide examples, as they might look subjective, but if you will take a look at them yourself and you will see that they are written for actual developers, not robots.

By the way Vue docs are written in seven languages. I myself think that English is enough (IMHO every developer should know English enough to read), but it's cool nevertheless.

CLI

From my previous article, you should understand that previously I was a fan of Angular CLI and was thinking that it's the best CLI out there, but:

  1. Sometimes it's very had to customize
  2. Angular and CLI is split into multiple @angular packages with different versions, which sometimes causes many problems with upgrade of CLI/Angular. Which is also quite complex already

On the other hand, Vue CLI 3 is the coolest CLI today:

  1. Customization is very easy and any package can be added at any time, thanks to the plugin system
  2. Simplicity and flexibility, thanks to webpack

Zone.js vs Vue Reactivity

Angular uses Zone.js to detect changes, which monkey-patches standard API's for that, like setTimeout.

This causes some problems:

  1. Difficulties with non-standard API, sometimes quite had to resolve
  2. Horrible stack traces
  3. IMHO this is a hack, and not the most elegant one

Vue doesn't need Zone.js, change detection works thanks to conversion of all data/state properties to Observer.

Sometime ago I was even a little disappointed by the lack of any magic after looking at the sources. On the top level, it's quite simple: Vue traverses through all of the properties with Object.defineProperty (exactly the case IE8- is not supported) and adds the getter/setter. Nothing more to add...

However, this approach has some caveats, but they are extremely clear described and easy to understand. Understand not only Vue, but the JavaScript in it's core.

Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` is now reactive

vm.b = 2
// `vm.b` is NOT reactive

Vue.set(vm.someObject, 'b', 2)

Also, note that Vue 2.6 will not have even these problems, because it will use Proxies and it will be possible to detect addition/deletion of the properties.

Rx and State Management

In Angular out of the box RxJS is a core of the framework, everything is Observable. Despite my love for Rx, many others and I had questions like: "Should this really be inside Angular core?". Especially at first, a lot of developers were just casting Observable to a promise via .toPromise(). In addition, the whole idea of single data stream isn't easy to get into, added on top of complexity of Angular itself.

At the same time, being such a massive framework, Angular doesn't provide an implementation of the most popular pattern to work with data - State Management. There is NgRx, but it became fully usable not so long ago - as a result one of our older projects even has a custom implementation of similar mechanism.

Now about Vue.

First - any RxJS fan can easily integrate it into Vue at any point in time, just by adding an additional package. Seconds - there's even Vue-Rx, allowing to use RxJS Observables along with data.

Regarding State Management there's an amazing official Vuex. It was a bit of a shock for me to know that there are also a lot of alternatives:

Drop In

Basically, this is the main upside (though some would consider this a downside) of Vue - everything is pluggable when needed.

Just add:

  1. Water
  2. RxJS
  3. Vuex
  4. TypeScript
  5. SCSS …

Everything you would need is always easy and fast to integrate.

Router

One of the most painful memories from Angular is router. Even though it has been rewritten three times, it is still horrible (yes, I am repeating myself).

I will not describe all of the problems in detail, but bear with me:

  1. There are no named routes (!) - one of strangest decisions was to remove named routes, adding a lot of extra problems with supporting large codebase

  2. Strange event system, dependent on the Event type

  3. Turning parameters into Observable - sometimes they are hard to work with, especially if you only need them once at the component initialization

  4. For navigations commands are used - very strange decision, which none other router use

  5. Lazy Loading of modules via their string names, given whole codebase is strongly typed...

    ... etc.

The main grief is that some features were present in past versions: like the working named routes and, for example route reuse parameter. Before it was just a route parameter reuse: false, but now you have to implement not-so-small class RouteReuseStrategy.

After looking at sources, it seems that a lot of these problems are related to the complexity and functionality of the router. So even if there were no such things as commands, feature set is still big, which makes it complex and heavy.

Vue router is super simple and it works.

Saying simple, it should be noted, that it may not have some features Angular router has. Fox example I was a little surprised that abstract: true parameter for route doesn't exist (especially useful for organizing tree structure for routes for stuff like breadcrumbs). Nevertheless, even if it is not present it's very easy to do via a component like:

// AbstractRoute.vue
<template>
  <router-view/>
</template>

Is it bad that there's no such functionality out of the box?

Yes and no, because this is easy to do yourself, and at the same time router is still easy and fast (compared to the above problems with feature-rich Angular router).

By the way, here is the cool thing:

	path: `url/sub-url/:id',
	component: MyComponent,
	props: true

Now MyComponent will have a new props - id, which will be a parameter from URL. This is an elegant solution because id will be reactive right away and component used would be the same as simple props - this.id, very easy (compare to Angular's ActivatedRoute).

Extend and reuse

On one hand, every component in Angular is a TypeScript class.

However, some TypeScript features are sometimes unusable or there are difficulties. First of all that is related to OOP - inheritance, abstract classes, member visibility. Most of problems are related to Dependency Injection and AOT compiler (god help you if you encounter them).

In Vue - instance configuration is an object - it's very easy to work with, extend and refactor. There are powerful features for reuse - Mixin's и Plugin's.

UI components

In Enterprise segment components usually have mockups, design, etc. Most of the time they are written from scratch, being honed for specific use case/product. But not every developer have leisure to write everything from scratch, especially this is about pet projects and prototyping.

This is where existing UI component frameworks come to help us. For Vue there's already a big list of them: Element, Vuetify, Quasar, Vue-Material, Muse, iView, etc etc.

Especially I would recommend Element and Vuetify, as they left a great impression: beautiful and stable components for every need, and with good documentation.

We also really like Buefy, based on the great CSS Bulma framework. Best used if you already use Bulma and want to add some components like DatePicker or TugInput.

In case of Angular - IMHO there are only two Enterprise-level component frameworks: Angular Material (Google) and Clarity (VMWare). Unfortunately, Clarity speed of development seem to have slowed down a bit, which saddens me even more regarding Angular perspectives in this aspect.

Real life experience and problems

And now about main problems from real life experience with Vue on live projects.

Too much freedom

From my perspective, the main Vue problem for today is too much of a freedom. Sometimes it's cool that you can do the same stuff multiple ways, but in reality this means that codebase can become quite inconsistent.

Specially exaggerated example: custom logic on some page can be added via component, it can be either local:

const Component = { created() { // logic ... }}

new Vue({
  components: [Component],

or global (available anywhere)

Vue.component('Global', { created() { // logic ... }})

There can be a local Mixin whic will do the same functionality

const mixin = { created() { // logic ... }}

new Vue({ mixins: [mixin],

but Mixin could be global too:

Vue.mixin({ created() {// logic ... }})

There are also plugins, which can do basically the same as global mixins:

const MyPlugin = {
  install(Vue, options) {
    Vue.mixin({ created() { // logic ... }})
}

Once again, this is not a real example, I completely understand the specific purpose for each of these features (and they are very useful), but which one to choose may not be obvious for a developer, especially for a newcomer.

Same example could be about computed vs watched property, they have different purpose but can do same stuff, and I've seen people who misuse them.

Code review is required

Even though we use Vuex, I've seen that it's hard for people not to use data() instead of state properties. This is more of speed question - it's obviously faster to add something to data, but most of the time it means that at some point in time this should be moved to state. Therefore, the refactoring to do this move is just time wasted, so it's much better to have logic in state right away (some simple things can be left in data though).

I think that the worst situation would be in projects without code review and lot of junior devs without much Vue knowledge. Most likely in a few months after start such codebase would be very unpleasant.

Unit tests

Also after Angular, it wasn't very convenient to mock some things in Jest. Specific example - local storage. Someone in our team solved it via googling this issue on GitHub.

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock,
})

I didn't find this solution to be nice, but it turned out that there's a more elegant one

global.localStorage = localStorageMock;

Yes, this is not the Vue problem per se, but an ecosystem one, compared to Angular. This is exactly what I would like to see in the documentation.

Cookbook

To be fair, Vue team is well aware about this problem and they solve it by writing the Cookbook with recipes. This is set of solutions for some popular tasks, practical FAQ of sorts.

Here are some examples: unit testing, validation and HTTP interaction.

However, for now recipes are quite basic and are quite lacking for some serious tasks. Tests are described and as a general overview that's fine, but the mocking above you would have to investigate yourself. I will describe validation later, but HTTP interaction is definitely is not covered deep enough.

I would say, that one good thing Angular thought me is to work with backend API via service. I think that is a good pattern, which greatly helps with code support and reuse. But given there's no DI, and to create instances yourself wouldn't be very wise - I would love to see this pattern in a Cookbook.

We mostly solved this problem for us by creating some local code conventions and best practices (links at the bottom of the article).

Dependency Injection

Regarding DI: after coming to Vue, I noted that mocking external dependencies is a little bit easier in Angular. This is fully dependent on the codebase and internal best practices, but sometimes in Vue this can be more complex (example above). Same is applied to the service pattern I just described.

However, I still don't think this is enough reason to take such a heavy and JavaScript-foreign concept to the front end.

Some people even recommend to use things like InversifyJS or vue-inject for Vue apps. While adding InversifyJS would be perfectly fine on Backend/Node, dragging all of this to an SPA doesn't seem to be a good idea for me.

Vue has DI

I was very surprised, while doing research for this article, to know that there's actually DI in Vue too:

// parent component providing 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// child component injecting 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

But for some reason there's little to none information on that in the web. There are some articles mentioning it, but nothing clearly describing the whole picture. For this reason, none of my Vue-developer friends ever heard of it.

I think this should be described starting in the "Essentials" section, not the "Edge Cases" in components.

TypeScript

I already wrote many times that TypeScript is very cool and useful, but the fact is - you need to know how to use it properly.

In Angular everything is tied with its experimental features (decorators), internal classes and hundreds (thousands?) of unnecessary abstractions.

In Vue TypeScript usage is much more logical - it's just an extension of developer options, without the need to rely in specific language features.

Problems with TypeScript in Vue

However, TypeScript usage in Vue isn't so easy today. Here are some problems, which we encountered.

First of all they still havent approved my Pull Request. Joking of course, that's my own issue

Two approaches

The main problem of TypeScript in Vue for today in my opinion is that there are two official ways to use it.

That is Vue.extend (typings that are in Vue out of the box and are supported with the library itself):

import Vue from 'vue'

const Component = Vue.extend({
  ...
})

and very similar to Angular decorator vue-class-component:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
  template: '<button @click="onClick">Click!</button>'
})
export default class MyComponent extends Vue {
  message: string = 'Hello!'

  onClick (): void {
    window.alert(this.message)
  }
}

I'm not a big fan of class-component and due to various reasons we use Vue.extend:

  1. class-component has own caveats
  2. We want to use default ES6 components as much as possible
    • And use the code from docs as is, without need to modify it for TS
  3. It's required that the team understands how Vue works without TypeScript
    • Especially since most of our developers has Angular background

Basically for the same reasons we don't use vue-property-decorator and vuex-class - we don't want to add additional TS complexity where it's not intended per se. We try to only use additional packages where they are required.

But Vue.extend(), on the other hand, has some own downsides:

  1. You cannot use component as a class (thanks CO), so no private, static methods, etc.
  2. It's sometimes a pain to use Vuex mappers like …mapGetters(), …mapState() etc. Usage sometimes breaks typings and causes strange errors. Waiting for the solution to be merged
  3. data() typing is not very convenient, since it's a function you need to create an interface describing the return value
  4. Proper props typings is basically not possible, since Vue requires a native JS type / Object / Array, but you want to add TypeScript interface, for now we use ugly solution with casting:
// Real type which will be available in myObjectProps
interface MyType = {...}

// data typing example
interface MyComponentData = {
  someBooleanProp: boolean;
}

export default Vue.extend({
  data(): MyComponentData { // Annotate return value of data()
    return {
      someBooleanProp: false
    };
  },
  props: {
    myObjectProps: Object as MyType // cast to TS interface
  },

Other problems

Here are some other problems with TypeScript:

  1. TSlint doesn't work with Vue out of the box - still can't run on .vue files. There are solutions with fork-ts-checker, but are not very nice
  2. ESlint for TypeScript is not very good. Both plugin and parser are very much in-development. A lot of core and vue rules break, but the main problem is - there are unknown errors. However we still use ESlint, just with some broken rules disabled - here's our ESlint config.
  3. Vuex store isn't typed in components, this.$store calls from components can be basically done with any payload.

Forms with Vuex

In general, forms aren't a problem of Vuex, but any State Management pattern implementation. It assumes you have a one-way data flow, but for forms, you would usually want to do two-way binding.

Vuex suggests two approaches.

Through binding value to a state value, and input even for update with commit:

<input :value="message" @input="updateMessage">
// ...
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

Alternatively, use two-way binding and v-model and to get value via getter and commit via setter:

<input v-model="message">
// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

We think that second approach is more convenient and neat. However, it is still quite verbose. Imagine using this approach with a complex form with 20+ fields... This will create a LOT of code for such a seemingly simple case.

Problem is a little bit less visible if we use mappers, i.e. mapGetters() and mapMutations(), but, as I mentioned above, for now thay doesn't work too good, and we had to find other solution.

We wrote a super-primitive mapper, which just adds getter (getter from state) and setter (commit mutation):

static mapTwoWay<T>(getter: string, mutation: string) {
  return {
    get(this: Vue): T {
      return this.$store.getters[getter];
    },
	set(this: Vue, value: T) {
	  this.$store.commit(mutation, value);
	}
  };
}

Which allows for shortening the code and describing field like this:

stringTags: Util.mapTwoWay<IDatasetExtra[]>(STRING_TAGS, UPDATE_STRING_TAGS)

And to use v-model:

v-model="stringTags"

Form validation

What was very surprising for me after coming from Angular - there's no form validation out of the box. However, the feature seems quite popular. Nevertheless, that's not a big problem, since there are already two popular solutions for it.

vee-validate

First - that's very similar to Angular template-driven from mechanism - vee-validate, allowing describing the whole validation logic in HTML.

<input v-validate="'required|email'">

I would say that this approach would really suited only for smaller/simpler forms. In reality, validation tends to become quite complex after some time and doing it in HTML can become problematic. In addition, it doesn't work with Vuex so good.

vuelidate

Second solution - Vuelidate. Very elegant way to validate practically any kind of component - the model is validated instead of some input.

Fun part - before discovering it we started writing something very similar oursleves.

<input v-model="name" @input="$v.name.$touch()">
import { required, email } from 'vuelidate/lib/validators'

export default {  
  data () {
    return {
      name: ''
    }
  },
  validations: {
    name: {
      required,
      email
    }
  }
}

I would really recommend using Vuelidate straight away if form validation is needed. It works great (with Vuex too), easy to plug-in and customize.

Main feat/problem of Vue

This is the main problem (I consider this a feat) of Vue - this is not an all-in-one framework, but only a library.

Out of the box there's no:

  • HTTP
  • Validation
  • i18n
  • ... etc?

Yes, there are officially supported:

  • Router
  • State But they are still separate packages, not a core.

On the other hand, Vue comes out of the box with such a cool thing like Animations ??

At the time I found it, I was quite amazed by the features of default animations mechanism in Vue. I would say that this is more flexible and powerful toolset, compared to Angular, for example.

Very cool example from docs:

Nightwatch and e2e

Small problem, again not related to Vue itself, which we found on real project.

Out of the box when generating the project via CLI you can choose between Nightwatch and Cypress for e2e testing. Even though I see Cypress as a best e2e testing tool today, [browser support is lacking]((cypress-io/cypress#310).

For this reason, we couldn't use it for production project as our customers use other browsers too. Therefore, we chose Nightwatch.

Once upon a time on Linux CI... our tests all failed for unknown reason (it was perfectly fine on Windows). And errors weren't informative at all. Later we found out that the problem is related to hashes (#) in URLs. And vue-router uses such URL's by default.

Unfortunately, this seems to be a problem with Selenium ir ChromeDriver, not a Nightwatch (another reason to look at Selenium-free tools like Cypress or TestCafe) but for today solution is to "clear" URL before opening a hashed one:

.url('data:,')
.url(client.globals.devServerURL + `/#/my-hashed-url`)

Our notes

Development speed

After Angular and great amount of boilerplate code, which we had to write, working with Vue seems fast and simple.

All decisions in the framework are directed for minimalism, as for the features, and for Developer Experience point of view. I would say that our team really became much more productive.

Community played a significant role here, as there are many existing solutions for a big variety of use cases.

What is missing?

What's Vue really missing these days is a good React Native alternative. There's no such kind of mobile development framework for Vue, at least with the same power/features.

Yes, there's NativeScript Vue, but it's far less powerful for today. There's also Weex but it's also is very much in development.

Performance

We also noticed that Vue is quite fast. Especially the startup time to load the "first page".

This is not the full table - full will have similar values between Angular and Vue, but startup is really much faster

You can find the whole test here: http://www.stefankrause.net/js-frameworks-benchmark7/table.html (apart from "great three" there are also many other frameworks to compare).

And also some links from more competent guys: GitLab, CodeShip, Alibaba, Xiaomi.

Conclusion

From all of the above we've come to some conclusions.

First - TypeScript usage in Vue today has some problems/downsides, but nevertheless is worth it. We use it on a number of projects now and even some going to production soon. Furthermore, with clear understanding of these problems and solutions for them, we learned to work with TS in Vue as fast and easy as we do with JavaScript. Still, I would love to see better TypeScript support in future versions.

We chose Vue nearly a year ago, and for the whole time we didn't regret it at all. But the amount of positivity Vue gave us rises every day.

Therefore, if you were still "doing things the old way" I would really recommend you to at least try Vue and feel the difference yourself.

Our code conventions

So that your experience would be even more pleasant, here are some of our code conventions, might be useful for you. Of course, they are very subjective, but it couldn't be otherwise.

Code style and application structure

Our own cookbook - now only contains service example, but will grow soon (some info isn't ready or on local resources).

Here's our Vue seed (TS, custom Jest, ESlint configs, etc), for now, since Vue CLI 3 isn't released yet, that's a repository instead of a template. Will be changed at the time Vue CLI 3 is released.

And that's it??

Yep. Thanks for taking your time reading this

PS: After reading this you may think that Vue has more downsides rather than upsides - that's not it, I just didn't want to write another praising-only article because there's plenty of them on the web already

@ftred001
Copy link

Awesome insights! Thanks a lot for writing this nice article.

@bennadel
Copy link

Really great write-up of your thought process and feature comparison. I'm currently trying to understand how Vue.js developers access Services without Dependency-Injection. Like you, I am finding almost no mention of it around the web. I know it's there; but, barely discussed. Trying to understand how people get one module into another module. I suppose they are just importing values directly as needed.

@danielfc
Copy link

danielfc commented Mar 15, 2019

Said it all. Couldn't be more accurate in Problems with TypeScript in Vue 💯

@nstuyvesant
Copy link

Nice article. Yesterday, I did an experiment converting our use of Vuex to Vue.observable. Would it be worth mentioning that State is now part of Vue's core as of 2.6.6? It is limited in that it mutates objects directly passed to it (at least until 3.x).

@SujitSingh
Copy link

SujitSingh commented Oct 23, 2019

Having a good understanding of both Angular and Vue, to me this Gist is old and biased towards VueJS. I appreciate a detailed elaborated comparison from someone who knows both Angular and VueJS world. But, I don't appreciate ignoring good parts of Angular like dependency injection, state management(without even using RxJS/NgRX), Forms, code structure/consistency and scalability.

It is also important to point out how hundreds of approaches followed by VueJS developer bringing more chaos to the already chaotic JavaScript world.
Also, the fundamental flaw in VueJS like "this" being "undefined" if using arrow function makes developers bang their head on their keyboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment