Up and Running with Vue.js
You don’t need another JavaScript framework. You’ve got one you’re using. Hopefully you’re happy with it. You just read eight other articles about other awesome, shiny, new, amazing, fabulous frameworks with astounding new features that will change your very existence into something incredible. Right? I did.
Vue.js is (like many other frameworks) worthy of all of those adjectives. But none of those are why you should use it. And really, maybe you shouldn’t. That will all depend on you and Vue and how you two get along. Let’s find out.
How I Met Vue
I build web things. Often. I’ve been building things for the web since the late ’90s. I was even writing JavaScript (well, JScript) on the server side pre-2000. I used it on the client side from the moment I could make animated gifs chase a mouse cursor.
However, even with all the history, I’d not met a JavaScript framework or library that I’d really fallen in love with until I met Vue.
It wasn’t because it was fast. It wasn’t because it was the “new hotness.” It wasn’t even because the code was simple and obvious. It was for one simple reason: It thought like me.
Data as state machine
This was the key part of Vue that won me over.
State machines are old awesomeness. Nothing new here. However, in most JavaScript frameworks, libraries, or even “vanilla code,” understanding the state of the system is like untangling all those wires under your desk — or worse.
Vue puts the state machine at its very core by using a plain JSON-compatible object. Essentially:
data == JSON.parse(JSON.stringify(data));
Vue then adds reactive binding via its templating system and provides methods for data observation using simple JSON Path-style watchers. The end result is an application that centers around its data and reacts to changes within that data.
Strikingly simple start
Another thing that won me over was the speed at which I could get my head around the sample code and explanations in the documentation.
For example, here’s a strikingly simple Hello World-style Vue application (view it on JSFiddle):
App.js
new Vue({ el: '#app', data: { message: 'Hello Vue.js' } });
App.html
<div id="app"> <p>{{message}}</p> </div>
That’s about as simple as it gets in any JS framework. The curly-brace style templating was very familiar from building entirely too many Mustache and Handlebars templates. The neat part with Vue, however, is that message
is immediately dynamically bound! No extra time defining state classes, interfaces, or magical pony meta-objects. Just some JSON and some HTML and some JS glue in the middle.
Don’t believe me? I’ll prove it by adding a bi-directionally bound message editor and only changing the HTML.
App.html
<div id="app"> <p>{{message}}</p> <input v-model="message"> </div>
The v-model
attribute dynamically binds the input (or textarea) field you’ve chosen to the data in your application, giving you direct access to manipulate the state of your application’s data-based state machine. Handy. To say the least.
That alone had me pretty sold, but obviously one could write it off as “neat.” However, as I dug deeper into the examples, documentation, and my own experience, I found that this simple, obvious approach brought me not only great happiness but also great reward in terms of speed of coding, understanding my own code, and ability to explain it to my friends and future self.
Vue successfully reduced my mental workload of bi-directional binding and application state into a single, understandable unit.
The best way to explain my utter exuberance over all this — and how it’s hopefully applicable to your day today — is likely to take this example a bit farther and see where we end up.
Shall we?
Make Messages
So. We’ve got a thing where we type stuff and it shows up on a screen. Neat!
What if we made it possible to record what we’ve written plus when we wrote it? Like, you know, note taking.
We’ll do that by:
- Expanding the data model to include: a) a log of messages in the main application and b) timestamps on messages
- A button for removing earlier messages
- And for bonus points, a Vue component to trigger a desktop notification displaying the note (because why not)
Sounds straightforward enough. Here goes.
Message logging model
Since we have this fabulous underlying “data as state machine” thing, let’s flesh out the model a wee bit.
new Vue({ el: '#app', data: { message: 'Hello Vue.js!', log: [{ value: 'Codeship is awesome!!1!', created: '2017-01-09' }] } });
This change expands on our existing code, so it should all still work. It adds the log
(just an Array) which contains the message log. I’ve prepopulated it with a particularly useful message.
Let’s keep iterating. Next up, HTML.
<div id="app"> <input v-model="message"> <button v-on:click="save()">save</button> <ul> <li v-for="(msg, idx) in log"> {{msg.value}} - <em>{{msg.created}}</em> <button v-on:click="log.splice(idx, 1)">X</button> </li> </ul> </div>
You’ll note I’ve removed the bi-directional bound display for message
, but left the input field and v-model
binding. I’ve also added a Save button and the list of messages. The list of messages is created by looping through the log
array using the v-for
property, with the object in the array set to msg
and the index of the object within the list set to idx
.
If you run that now, you should see one message in the list, a button to remove that message, and the message box (prepopulated with a default) to type in a new message as well as a Save button which doesn’t (yet) work.
Let’s make the Save button work. Back to the JS:
ew Vue({ el: '#app', data: { message: 'Hello Vue.js!', log: [{ value: 'Hi there, Codeship!', created: '2017-01-09' }] }, methods: { save: function() { this.log.unshift({ value: this.message, created: (new Date).toISOString() }); } } });
Now try it! Pretty slick, right? We’re simply manipulating the state of the data, and the output is reacting to that state change.
However, I spot some “data smarts” in that save()
method. Let’s see if we can put that in a tinier place, so we don’t have to read the save code to understand (or reuse) this app’s date format.
Datum date datum
Remember that bit about data as state machine? It gets better.
That created-date formatting living inside the save method is a bit of a “smell” (i.e., in want of refactoring). Consequently, it would smell less if it lived on its own. Vue has just the thing for that: computed data.
Here’s the updated app code showing the new created
computed property:
new Vue({ el: '#app', data: { message: 'Hello Vue.js!', log: [{ value: 'Hi there, Codeship!, created: '2017-01-09' }] }, computed: { created: function() { return (new Date).toISOString(); } }, methods: { save: function() { this.log.unshift({ value: this.message, created: this.created }); } } });
We’ve added a new computed
object which contains a single created
property that returns the current date in the One True Date format, also known as ISO 8601. So far. So obvious.
Now focus in on the third line of the save()
method. See that? We’re accessing the new computed created
value off of this
, and (you guessed it!) it’s returning the current date in ISO 8601.
The this
object in Vue is set to the current “Vue Model” (often abbreviated as VM). This object allows direct get-ing and set-ing of the VM’s data as well as access to its methods (e.g., this.save()
works).
But this is still just the world of a single Vue application. We’ve not yet added a component.
Let’s add a component that could be useful in other applications. How about a desktop notification button using an emoji! That sounds super useful!
Desktop notificator
The Vue component below could be used in any Vue application. It accepts two properties: message and title. These map into the JS Notifications API you can see used in the notify
method on the component.
The template
is inline this time, for legibility here and for keeping this component completely contained in a single file — and since it’s just so plain simple.
Vue.component('desktop-notificator', { props: ['message', 'title'], template: '<button v-on:click="notify()">??</button>', methods: { notify: function() { if (Notification.permission !== "granted") { Notification.requestPermission(); } else { new Notification(this.title, { body: this.message, }); } } } });
If you’re coding in your own JSFiddle (or similar) you can copy/paste that into the main JS above the new Vue
bit. Or, if you want, you can toss it in its own file and load it via a script tag (below the Vue.js script tag of course).
Once you’ve got it added, you can use it within this (and any other) Vue application by adding code similar to this:
<desktop-notificator v-bind:message="msg.value" v-bind:title="msg.created"></desktop-notificator>
The v-bind:
prefixed attributes map the internal data model (message
and title
) to the parent application (or components) values: msg.value
and msg.created
(in the case of this app).
Let’s take a look at it within the wider awesomessness of this little app’s HTML:
<div id="app"> <input v-model="message"> <button v-on:click="save()">save</button> <ul> <li v-for="(msg, idx) in log"> <desktop-notificator v-bind:message="msg.value" v-bind:title="msg.created"></desktop-notificator> {{msg.value}} - <em>{{msg.created}}</em> <button v-on:click="log.splice(idx, 1)">X</button> </li> </ul> </div>
Try it out! You’ll need to allow desktop notifications for it to work (which you probably already knew).
Simplification
We’ve made it this far. We’ve got a working application that does extra neat things like desktop notifications. We’ve seen a bit of Vue’s component system and its bi-directional binding enabled templating system of greatness.
But there’s more. Lots more.
For now, we’re going to take a quick look at some niftiness to streamline the template code we’ve just written to make it simpler — though (admittedly) a bit more idiosyncratic.
Idiosyncratic awesome
Every JS framework is unique. Often the uniqueness can feel strange or awkward in other people’s code or examples in documentation. That’s likely how you’d have felt if I’d started with this code.
<div id="app"> <input v-model="message"> <button @click="save()">save</button> <ul> <li v-for="(msg, idx) in log"> <desktop-notificator :message="msg.value" :title="msg.created"></desktop-notificator> {{msg.value}} - <em>{{msg.created}}</em> <button @click="log.splice(idx, 1)">X<button> </li> </ul> </div> Some of thev-*
attributes remain, but we’ve replaced thev-on
andv-bind
attributes with a short hand notation:v-on:
is replaced by@
andv-bind:
is replaced by:
. Weird looking, right? However, as you spend more time with Vue, you’ll find this notation actually makes the template code more legible. There are also handy syntax highlighters likely available for your preferred editor which make these short-hand attributes stand out a bit more.
Even great idioms
We’ve built this simple application and its component using two files: one for the JS code and one for the HTML (which you could even combine). The Vue world, however, has a handy command-line tool called vueify which, as you might surmise from the name, works a bit like browserify, but with lots of Vue-focused fabulousness. For instance, vueify allows you to work with single .vue
files which contain all the JS, CSS, and HTML you need for a single component. It’s obviously super handy for components like the one built above. Here’s what desktop-notificator.vue
would look like:
<template> <button @click="notify()">??</button> </template> <script> export default { props: ['message', 'title'], methods: { notify () { if (Notification.permission !== "granted") { Notification.requestPermission(); } else { new Notification(this.title, { body: this.message, }); } } }; </script>
The template code now lives inside a template tag, and the script tag now holds our JS code, which I’ve changed to use ES6/ECMA2016 which vueify is happy to transpile for me. Additionally, vueify allows you to mix in other preprocessors such as Jade, Stylus, CoffeeScript, and many more.
If you think in preprocessed languages, adding vueify to your coding kit might increase happiness by at least +10.
Vue Thinks Like Me
When I found Vue, I found a framework that thinks like I do.
Data is central to what I design. Everything else pivots around it. Using watchers to react to change and computed values to generate unique additional value puts my “prescriptive” code in a place where I could still see my “descriptive” data.
Templating is something I (actually) enjoy, and I’ve found Vue’s bi-directional binding, custom attribute-based integration, and web-component-like application construction to be refreshing. Containing elements of the UI within components that communicate their state through shared data and events felt right (to me at least).
Lastly, the Vue community, while still relatively small, has also been welcoming and eager to continue Vue’s future and grow the surrounding ecosystem by contributing to “official” add-ons like Vuex (a Flux-style state manager) and vue-router (for history and page-state management). There’s also an ever-widening world of component-based code for your Vue-based applications.
Don’t Use Vue — unless
It thinks like you do. If not, please use something that suits you. You’ll get more done, be happier, and consequently get more done. Enjoy!
Reference: | Up and Running with Vue.js from our WCG partner Benjamin Young at the Codeship Blog blog. |
I would thank you very much for this article ! When I read about Vue, I felt immediately attracted by it, without understand why … but now your plain and clear explanation has showed me the inner beauty of it. Thank you again !
Great post ! Simple yet effective example that made me discover (and start to love) Vue.js