Dave R. bio photo

Dave R.

Software Enthusiast.

Twitter Github Stackoverflow

An initial look into Vue.js, a popular MVVM UI framework, which seems to be going from strength to strength.

background: My goto UI framework is Aurelia, and am interested in how this stacks up.

(if i miss anything tweet me)

TLDR;

  • Vue is epic, and is closing the gap to Aurelia
  • Developer experience is great.
  • Class components are the best way (and will become the defacto)
  • I miss the dependency injection, loggers and event aggregator that Aurelia provides
  • The 1 file for the Vue, is really nice (enforces smaller components)

code

all code is not production quality.

shout outs to:

The App

To enable a smack-down, I mean any observations, we will develop a small simple todo app (surprise!). In addition we can use provided libraries to help highlight how things work in these frameworks.

Vue

Aurelia

setup overview

  VueJs Aurelia
CLI official official
VS Code Vetur aurelia
Chrome Vue.js devtools aurelia inspector
Materialize Vuetify Aurelia Materialize Bridge
Project language Typescript Typescript
bundler webpack webpack

Observations

what should we observe

In this post I wanted to look how vue.js supports the developer, in creating a maintainable application.

  • Concepts
  • project setup / documentation
  • state
  • data-binding
  • routing
  • components
  • tools and debugging

as mentioned, we will baseline these observations via comparison, using Aurelia as a yard post.

Concepts

Vue is similar to Aurelia (a component framework with templating/data-binding). It is not like React (where react everything is in Javascript).

Vue has many of the features that Aurelia provides.

VueJs Aurelia
Components Components (split into 2 files, .ts / .html)
Filters Value Converters
Custom Directives Custom Elements
scoped CSS scoped CSS
Slots Slots
Data-binding Data-binding
Plugins Plugins
  Dependency Injection
Router Router
Transitions Transitions
  Event Aggregator
mixins mixins
  loggers

For Vue there is a workaround to implement a event bus.

page life cycle

Both frameworks offer places where you can intercept the components life cycle.

VueJs Aurelia
beforeCreated()  
  constructor()
created() created()
beforeMount() bind()
mounted() attached()
beforeUpdate()  
updated()  
beforeDestroy()  
  detached()
destroyed() unbind()

(i have assumed a couple are similar)

project setup / documentation

Vue provides a wizard setup via its Cli, this is comparable to Aurelia’s wizard.

An interesting point during the vue setup was that the routing was an optional extra, I assume this is part of the adoption strategy where you can write 1 page of a legacy app using Vue and transition from there.

The main observation at this point, is Typescript is not the primary language for both frameworks, but its how this is supported by both.

Aurelia has been built with this in mind and provides excellent support, in both intellisense (d.ts files) and documents (all examples are in EsNext and Typescript)

Vue in this version (2.x) offers some TypeScript support, there is intellisense (d.ts files), but not much in the ways of documentation (1 page).

At the time of writing Vue is being re-written from scratch (3.x) in Typescript. The Class way of writing a component will become the defacto, more TS friendly. (from a personal view this seems much cleaner than the current module default).

Other than that the rest of Vue’s documentation is spot on (as too is Aurelia’s)

Structure

Both cli’s provide a starter project structure of which you are free to change as required.

State

Vue’s Class way of writing components makes this very similar to working with Aurelia. If we revert to the current default then we would need to structure our module/typescript-class in a particular way, for example our state would need to be stored in the data attribute.

OLD way (vue)

export default {
  //scoped variables in this component
  data () {
    return {
      items: [],
      newItemContent: ""
    }
  },
  
  //rest of the module  
}

The developer does not have to manually implement an observer pattern. The ViewModel is modified on the fly with observers, on the properties what are data bound similar again to Aurelia.

New way (vue)

@Component({})
export default class Todo extends Vue {

  //the data is now just (natural) properties on the class
  items: Array<TodoItem> = [];
  newItemContent: string = "";

  //rest of the class
}

FLUX state

Although the app does not implement this, it is something that is key to all SPA’s, and from the docs you can see that Vue has Vuex, which integrates with their development browser tool.

According to Aurelia’s docs, the store offered by Aurelia simplifies the solution by missing our the mutation stage/functions.

Data binding

  VueJs Aurelia
text { {item.name} } ${ item.name }
dynamic style :style="attributeName" style="text-decoration:${ item.isComplete ? 'line-through' : ''}"
dynamic class :class="[ item.isComplete ? 'strike' : 'no-strike' ]" class="${item.isComplete ? 'strike' : 'no-strike'}"
click function @click="addItem()" click.delegate="addItem()"
repeater <v-card v-for="(item, index) in items"> <md-card repeat.for="item of items">
value binding v-model="newItemContent" value.bind="newItemContent"
value converter { { currentDate | dateFormat } } ${ currentDate | dateFormat }

(on the double braces, i had to add a space for this bog to parse it correctly)

both support similar capabilities, I found Aurelia being a fractionally more flexible, and i appreciated the use of the ${}.

My initial small issue I had was the short hand for v-on and v-bind in Vue, these are not completely consistent, but is optional.

Vue normal Vue short-hand
v-bind:disabled :disabled
v-on:click @click

their has been a tonne of thought behind all of these, and the more you use the framework the more this will make sense.

Routing

Not much to mention here, Vue supports this really well, in a very similar way to Aurelia

All you need to do is setup a router and then add <router-view></router-view>

Components

Both these frameworks are just components, which is just epic.

Vue combines all the parts of the component into a single file (the view, view-model and css) which at first I did not like, however its growing on me, as it should make developers consider how large their components are.

Vue component layout, 1 file

<template>
    <div>
        <!-- the template / view -->
    </div>
</template>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

  //CSS for this component
  .strike {
    text-decoration-line: line-through;
  }
  
</style>

<script lang="ts">
import Component from "vue-class-component";

@Component({})
export default class Todo extends Vue {
    //impl
}
</script>

The main point which i have already mentioned is using the Class Component which made these experience way better and brought this almost on-par with Aurelia.

Aurelia component layout, 3 files

---todo.html---
<template>
    <require from="./todo.css"></require>
    <div>
        <!-- the template / view -->
    </div>
</template>

---todo.css---
.strike {
    text-decoration-line: line-through;
}


---todo.ts---
export class Todo {
    items: Array<TodoItem> = []
    newItemContent: string;

    addItem(){
        //impl
    }
}

Both frameworks provide support for creating re-useable elements/components, one thing i liked better with aurelia was how bindable properties worked.

Vue

---component---
@Component({
  props: ['dateTime']
})
export default class MomentAgo extends Vue {
 
    beforeMount() {
        //note the <any>this, as the props is not part of this class.
        this.momentValue = moment((<any>this).dateTime).fromNow();
        //do more things
    }

    destroyed() {
        //do things
    }
}

---use---
<moment-ago :dateTime="item.dateTime"></moment-ago>

Note how the Aurelia code is very similar, except for the bindable property is located inside the class, and therefor compiler safe/refactor-able.

Aurelia

----component---
@customElement('moment-ago')
export class MomentAgo {
    @bindable value: Date; 

    bind() {
        //cleaner code, and compile safe.
        this.momentValue = moment(this.value).fromNow();
        //do things
    }

    unbind() {
        //do things
    }
}

---use---
<moment-ago value.bind="item.dateTime"></moment-ago>

Tools and debugging

Both have Vs Code and Chrome plug-ins, both are equally comparable.

Vue’s chrome plugin looks amazing, and provide a lot of information.

Aurelia’s plugin is epic too.

Conclusion

Vue is simply epic, and i look forward to V3, with more typescript support.

It is very similar to Aurelia, in terms of concepts, and I was able to create components which had a direct comparable in both applications.

However i do miss some of the things Aurelia provides (dependency injection, event aggregator and logging). In larger SPA’s these features become tools in which support decoupled modular plugins, and make them maintainable.

I did not really look into the plugin support. I was only able to observe a small portion via the Materialize UI plugin, which registered its components globally. this was not enough to make any real observation.

At this point I would strongly recommend people to look at Aurelia, and I would like to see what a Vue person would make of Aurelia.