I have always been a fan of simplicity when it comes to js frameworks. I guess that’s one of the reasons that Aurelia appeals to me. However, frameworks are inherently complex. They are usually opinionated (that’s part of the point of a framework, right?) and make a lot of decisions for you. The benefit is of course that many of the architectural design desicions are already taken and you (as a developer) can simply focus on writing code.

That said, a framework is not always the right choice. There’s been some heated debate on this over the years, but I tend to agree with Sean at planningforaliens in the statement

Using a JS framework should never be your default decision.

Instead focus on what you want to achieve and use the right tools to do just that and nothing more.

I’ve been a long time fan of Knockout.js. It’s been around for a long time, it’s small (~22kb min+gz), it’s simple and it gets the job done. It does not bring uneccessary bloat to my code base. I’ve used it for lots of projects and never been dissatisfied. Lately though I’ve been working with Vue.js more and more. Almost everything that has irked me slightly about Knockout, Vue does better, faster and simpler.

Versions

In this article I'm using the release candidate of version 2.0 of Vue. All of the code in this article is 100% backwards compatible with Vue 1.0 though.

Here are a few examples.

//Binding a model to a view in Knockout
ko.applyBindings(new someModel());
//or perhaps more commonly
ko.applyBindings(new someModel(), document.getElementBydId('elementToBindTo'))
//Binding to a view in Vue
new Vue({
    el: '#elementToBindTo'
});

Much cleaner and simpler in Vue in my opinion. Calling the ko.applyBindings() with document.getElementBydId is a nuisance, especially when unit testing.

//Creating a model with observables in Knockout.
function someModel() {
    this.someProp = ko.observable();
    this.someMethod = function() { this.someProp('banana'); }
}

//Vue
new Vue({
    el: '#elementToBindTo',
    data: {
        someProp: ''
    },
    methods: {
        someMethod: function() {
            this.someProp = 'banana';
        }
    }
});

A bit more verbose in Vue, but again, much cleaner and the separation of methods and data makes it easy to find what you’re looking for when you have a complex model. You also do not need to wrap your properties in a ko.observable call. This is good but does have some drawbacks in terms of browser compatability. I’ll get back to that.

<!--Knockout-->
<span id="elementToBindTo" data-bind="text:someProp"></span>

<!--Vue-->
<span id="elementToBindTo">{{someProp}}</span>

The difference here is not huge and not as apparent as it would be if we had several properties. Lets compare a slightly more complex example.

<!--Knockout-->
<button id="elementToBindTo" data-bind="text:someProp, enable: shouldBeEnabled, css: { loading: isLoading }, click:someMethod"></button>

<!--Vue-->
<button id="elementToBindTo" v-bind:class="{loading: isLoading}" v-bind:disabled="!isLoading" v-on:click="someMethod" >{{someProp}}</button>

<!--Vue with shorthands-->
<button id="elementToBindTo" :class="{loading: isLoading}" :disabled="!isLoading" @click="someMethod" >{{someProp}}</button>

As you can see Knockout puts all its databinding in one attribute data-bind which makes it hard to read when the different bindings start to pile up. Vues syntax is a bit more verbose, but you can use shorthands to make the attributes very unobtrusive.

But how can Vue add data binding functionality to apparently plain javascript objects? The answers lies in Object.DefineProperty. Vue traverses the entire data object and converts every property into getters and setters using Object.DefineProperty adding logic to each for change-notification and dependency-tracking.

Heads up

Object.DefineProperty is an ECMAScript 5 feature and thus not available in older browsers such as IE 8. So if you're stuck having to support such ancient browsers for some unfathomable reason then you're out of luck with Vue and we all pity you.

We can actually observe this happening in our console if we inspect the vue data object:

Notice how the properties are actually get/set methods instead of primitive values?

If you want to delve even deeper into how Vue applies change tracking there’s an excellent article in the official docs.

Lets build something

Okay, so we have scratched the surface of Vue. Now lets build something where we put it all together.

<div id="hall-of-infamy">
    <ul>
        <li v-for="villain in villains" :class="{eliminated: villain.eliminated}">
            {{ villain.name }}
        </li>
    </ul>

    <input v-model="newVillainName" @keyup.enter="addVillain">
</div>
new Vue({
    el: '#hall-of-infamy',
    data: {
        newVillainName: '',
        villains: [
            { name: 'Cersei Lannister', eliminated: false }, 
            { name: 'Lex Luthor', eliminated: false },
            { name: 'Al Capone', eliminated: true }
            ]
    },
    methods: {
        addVillain: function(){
            var villain = this.newVillainName.trim();
            if(villain){
                this.villains.push({name: villain, eliminated: false});
                this.newVillainName = '';
            }
        }
    }
});

We implement an html snippet that will contain our list of villains, using the v-for attribute to loop through the contents of an ordinary javascript array. As you can see we can then access each villains properties.

On the input we make use of the @keyup binding (remember @ is the shorthand for v-on:) with an event modifier .enter. This means we’re listening specifically for when a user hits the enter key on our input. A very convenient way of abstracting away the boilerplate code of if(e.keyCode === 13). Vue provides several very handy event modifiers.

The model instance then provides the view with the data it needs and a handler for the keyup event.

Notice how we use the v-model attribute to bind the input to the newVillainName property. This is actually nothing more than syntactic sugar for the more verbose <input v-bind:value="newVillainName" v-on:input="newVillainName = $event.target.value">.

Components components components

If you’ve been working with Knockout in the past chances are that you’ve created your fair share of Knockout components. Reusable, small widgets with a custom tag name that can contain their own view and model. Vue has the same concept and in fact takes it a bit further with single file components that encapsulate everything from the html template to the css in a single self contained file.

Lets consider our example above briefly. How can we extract the presentation and logic of a villain away from the list presenting it? Lets turn it into a component!

To register a component globally (we won’t be looking at local components in this post, maybe another one) we call the Vue.component method like this:

 Vue.component('villain', {
     data: function(){
         return {
             name: this.villainData.name,
             eliminated: this.villainData.eliminated
         }
     },
     props: ['villainData'],
     template: '<li :class="{eliminated: eliminated}">{{name}}</li>'
 })

This component can then be used in our html like this:

<villain villainData="{name: 'something', eliminated: false}" ></villain>

Notice that data needs to be a function for components. This is to make sure that each component gets its own instance of the data object, otherwise they would share state and changes to one would update all components of the same type!

Another interesting thing is the props property, this is how we pass data to our component from an outside scope. In our case we pass the data about the villain to our component and later use that to set the properties of our component. This is not strictly necessary, we could use our villainData directly in our component like this:

 Vue.component('villain', {
     props: ['villainData'],
     template: '<li :class="{eliminated: villainData.eliminated}">{{villainData.name}}</li>'
 })

This would make sure that any changes to the villainData made by the parent would flow down to the child component. However as objects are passed by reference any changes made to the villainData by the child would mutate the object for the parent as well and this might cause unwanted side effects. Vue will actually warn you if you try to mutate a prop inside a child component.

In our simple case it makes no big difference, but it’s important to understand the distinction.

Our complete example with a component would now look something like this:

<div id="hall-of-infamy">
    <ul>
        <villain v-for="villain in villains" :villainData="villain"></villain>
    </ul>

    <input v-model="newVillainName" @keyup.enter="addVillain">
</div>
Vue.component('villain', {
     props: ['villainData'],
     template: '<li :class="{eliminated: villainData.eliminated}">{{villainData.name}}</li>'
 })

new Vue({
    el: '#hall-of-infamy',
    data: {
        newVillainName: '',
        villains: [
            { name: 'Cersei Lannister', eliminated: false }, 
            { name: 'Lex Luthor', eliminated: false },
            { name: 'Al Capone', eliminated: true }
            ]
    },
    methods: {
        addVillain: function(){
            var villain = this.newVillainName.trim();
            if(villain){
                this.villains.push({name: villain, eliminated: false});
                this.newVillainName = '';
            }
        }
    }
});

We could now proceed to add logic and update the view for the villain component separately and without affecting our main model instance.

The component above is simple, but you can easily imagine a more complex scenario where having the template inline in the javascript could become cumbersome. The CSS needed to style the component is also nowhere to be seen in our current example. Vue addresses both of these issues by introducing single file components. You could put your entire component in a .vue file and use build tools like Webpack to transform it into plain javascript, css and html resources.

Our example component above would look something like this as a villain.vue file:

<template>
    <li :class="{eliminated: villainData.eliminated}">{{villainData.name}}</li>
</template>

<script>
module.exports = {
     props: ['villainData']
}
</script>

<style scoped>
.eliminated {
    text-decoration:line-through;
}
</style>

To be able to encapsulate everything related to a single piece of reusable code like this is very powerful. However it does increase the complexity of your build process and requires some very specific tools installed (NPM, webpack, vue-loader, babel…). It is certainly not suitable for every project. That said, for projects driven entirely by javascript this encapsulation might very well be worth the increase in build pipeline.

How does it compare?

So how does Vue stand up to other popular frameworks and libraries such as Ember, React and Angular? There’s a great comparison already on the vue.js official site that you can find here. This is of course slightly biased as it’s written by people already invested in Vue. However, numbers just don’t lie, have a look at this speed and memory comparison of most of the popular frameworks.

As you can see Vue is one of the best in class and faster than both Angular, Angular2, Aurelia, Ember and React.

So, it’s fast, simple, small and powerful and this article barely scratched the surface. There’s lots and lots more to look at: mixins, custom directives, computed properties, filters, unit testing etc. but enough for one day (or post). I’m sure I’ll get back to many of these concepts in future blog posts.

Now, do you Vue yet?