WD
WD

Adventures with Vue.js

New Javascript frameworks: ruining house party conversations in San Francisco since the mid 2000’s.

Unfortunately, I do spend just enough time making rough prototypes that jQuery is a headache — anything beyond a simple form / AJAX query quickly becomes a nightmarish bowl of Javascript spaghetti.

Learning your first frontend JS framework with a build chain is an excercise in in realizing that browsers were never really meant for enterprise-grade development, and that what we have today is an absurdly fragmented mess of “tools” used to try to make that experience better.

And yes, this is still how it feels to learn JS in 2017.

Yet the world keeps on turning, and browsers aren’t about to change quickly enough to ignore learning these things. Given that (and armed with a free weekend), my question was, “great, what should I learn?”

Being A Lemming

We developers are a bit like lemmings and like to follow where the bulk of open-source development is going. Following a trendy, well-supported framework gives you benefits like:

  • Fewer bugs and richer functionality
  • Better ecosystem around community-made plugins
  • Roadmap transparency
  • More StackOverflow question & answers
  • More StackOverflow question & answers
  • More StackOverflow question & answers

I kid. Well, only sort of.

The elephant in the room is certainly React. Compared to Vue, React is killing it among serious frontend devs who actually have build chains, transpilers, and other voodoo that require NPM:

Google, tells a similar story:

However among hip GitHub users, we see in the past couple of years Vue growth seems to be accelerating. Looking at stars (made with the fantastic Star History tool) we get rough proxy for popularity with devs:


Star History

We see that in the span of three short years, React has captured the focus of frontend devs (edging out Backbone & Angular.js), but the winner of highest rate of growth goes to Vue.js.

Comparing issue close rates on Github:

Framework Open Issues Closed Issues Dangling Issue Ratio
React 581 3,816 0.152
Vue.js 61 4,395 0.0139


Vue.js has a 10x smaller open to closed issues ratio (61 open issues and 4395 closed vs. 581 open React issues and 3816 closed at time of writing), which could suggest a faster rate of improvement and/or a higher level of community input.

Well, the point of this post isn’t to convince you Vue.js is better than React or vice versa. Here’s Vue.js’s take and another popular “showdown” article.

There’s excellent reasons to use either. Vue.js is more easygoing and nimble, but giant projects might be better off with React or the wary folks who value the stability of a large company (Facebook) as its maintainer.

Moving on, performance benefits of Vue.js include:

  • ~25kB size minified (as opposed to 37 - 48kB with React)
  • 2x faster rendering than React on average

Benchmark for rendering a list of 10,000 items 100 times:

In either case, Vue.js is in the top two for frontend JS frameworks and if it’s worth a taste of how to use it, continue on below.

Just don’t bring it up at a house party.

A Taste of Vue.js

Here’s a minimal setup:

<!DOCTYPE html>
<html>
  <head>
      <script 
        src="https://npmcdn.com/vue/dist/vue.js">
      </script>
  </head>
  <body>
    <div id="myApp">
      <p>{{ message }}</p>
    </div>
    <script>
      var application = new Vue({
        el: '#myApp',
        data: {
            message: 'Hello there',
        },
      };
    </script>
  </body>
</html>

What’s going on here? Quite simply we’re importing the Vue.js library, binding the application instance to an element in our DOM (using a CSS selector), and rendering some data.

Vue.js is creating a virtual DOM for all the elements to be contained inside #myApp. This has advantages over operating on the normal, vanilla browser DOM since Vue will now be able to tie together bits of data to elements, intercept events, and render custom elements we create.

Let’s add some event listeners and custom methods.

Adding Listeners, Custom Methods, and Binding to Data

<!DOCTYPE html>
<html>
  <head>
      <script 
        src="https://npmcdn.com/vue/dist/vue.js">
      </script>
  </head>
  <body>
    <div id="myApp">
      <h1>An Example Vue App</h1>
      <p>{{ message }}</p>
      <input v-model="message">
      <button @click="myCoolMethod()">
        Click to Alert 
      </button>
    </div>
    <script>
      var application = new Vue({
          el: '#myApp',
          data: {
              message: 'Hello there',
          },
          methods: {
            myCoolMethod: function() {
              alert('Alerts are annoying');
              // we could run some asynchronous code here
            }
          }
      };
    </script>
  </body>
</html>

We added:

  • @click to our button: this sets up a click listener and executes the code inside from our application instance
  • v-model input: this is the way we bind the value of this input to the data.message string of our application. You’ll notice if we change the characters in the browser, we change the data in the model, which automatically reacts by re-rendering our <p> tag! No effort needed!
  • methods function myCoolMethod

Control Logic in our Template

Let’s deal with more complex data and conditionally output a bulleted list with each element using a for loop:

<!DOCTYPE html>
<html>
  <head>
      <script 
        src="https://npmcdn.com/vue/dist/vue.js">
      </script>
  </head>
  <body>
    <div id="myApp">
      <ul v-if="messages.length > 0">
        <li v-for="msg in messages">
          Message: {{ msg }}
        </li>
      </ul>
    </div>
    <script>
      var application = new Vue({
          el: '#myApp',
          data: {
              messages: [
                'Hello there',
                'How\'s it going?',
                'What\'s the deal?',
              ]
          }
      };
    </script>
  </body>
</html>

You’ll notice the condition v-if="messages.length > 0" allows us to put arbitrary conditions inside of the evaluation statement on the custom v-if directive.

All of these are nice, but how do we deal with reusing elements - what if our data was more complex than a list? What if we wanted object-like encapsulation for each of our data items?

Enter components.

Components

From the Vue.js documentation, a small example with three counters”

<div id="example">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>

<script>
  Vue.component('simple-counter', {
    template: '<button v-on:click="counter += 1"></button>',
    data: function () {
      return {
        counter: 0,
      }
    }
  })

  new Vue({
    el: '#example'
  })
</script>

Here we’ve globally declared a component simple-counter which we can freely use multiple times in our DOM - each with their own internal state.

If you run this you’ll see that you can individually update counters and see that the <simple-counter> tags simply get replaced with what’s inside the template.

This method of using strings as templates quickly gets messy, so we’ll usually use the CLI along with single-file templates.

Getting Organized & Transpiling with the CLI Tool

As you can see, our example applications above are going to get a bit unwieldly as we keep adding data, methods, and logic.

Vue provides a CLI tool (vue-cli) that helps us set up larger projects. It helps do deployment packaging like combining all our code into a single file and minifying it. We can even write syntactically more palatable JS with ES6 and have it transpiled down to boring old, browser-compatible ES5.

First install the CLI tool and create a project folder:

$ npm install -g vue-cli
$ vue init webpack-simple myproject

We could use a few different types of initialization types, but for this we’ll use webpack. This command will create a folder like the following:

$ tree -L 2
.
├── index.html
├── package.json
├── src
│   ├── App.vue
│   └── main.js
└── webpack.config.js

Now we can put our components into single-file template .vue files structured like this:

<template>
  <!-- Your HTML elements go here -->
</template>

<script>
  export default {
    // Your data, methods, etc all go here

    // for example, using ES6 syntax for our data object:
    data() {
      return {
        variable: 0,
      }
    }
  }
</script>

<style scoped>
  // Any CSS you want applied to ONLY this 
  // type of component goes here
</style>

Generally we’ll have a root component file called App.vue and a main JS file that creates our Vue instance. Together the flow looks like:

Our index.html:

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="app">
    </div>
    <script src="/dist/build.js"></script>
  </body>
</html>

Then App.vue:

<template>
    <div class="container">
        <!-- ... stuff goes here ... -->
    </div>
</template>

<script>
    import MyComponent from './components/MyComponent.vue';
    export default {
        components: {
            'my-component' : MyComponent,
        }
    }
</script>

<style scoped>
</style>

and finally main.js:

import Vue from 'vue';
import App from './App.vue';

new Vue({
  el: '#app',
  render: h => h(App)
})

and we run a development server in this directory by running

$ npm install  # install node modules locally
$ npm run dev

This not only serves the page on localhost:5000/index.html, it also starts a hot-reload server so as you edit code, the page updates. No more CTRL+C, up arrow, and hitting ENTER dance everytime you change a file!

Building for Deployment

To actually package up our JS into a single file we use:

$ npm run build

This puts all the JS together in the dist/build.js folder, and this Javascript file could be uploaded to your static file store or CDN - it’s all you need to run and use everything contained in your project folder created by the Vue CLI tool.

Other Cool Features of Vue

Also know that Vue is quite full-featured and comes with a lot of amazing stuff:

  • Communication between components with events
  • Custom directives (create your own v-bind-type directives)
  • Filters to apply functions to data:
  • Mixins: share code among different components
  • Animations: using only minimal CSS
  • HTTP requests: this.$http.get(URL, function(...) { ... });
  • Vue Router: for single page applications
  • Managing global application state with Vuex (like Flux or Redux)

Wrap Up

This is a good preview of the Vue is, but if you want to learn obviously there’s a lot I didn’t mention here.

I can’t recommend highly enough Maximilian Schwarzmüller’s Udemy class on Vue.js 2.0 (newest version) - I was able to go from never having used a true JS frontend framework or ES6 to being quite comfortable building application and setting up everything in just a weekend. I’d recommend watching at 1.25x speed though.

It’s overall an impressive framework that is light, performant, fully-featured, intuitive, and quickly growing.