Vue.js

Reactive Components for Modern Web Interfaces

Vue.js highlights

Vue.js – Intro

  • View-layer library
  • Simple & beautiful API
  • Lightweight (~24kb min+gzip)
  • Fast
  • Reactive
  • Component based

How to start?

Vue.js – Basics

var object = {
  message: 'Hello world!'
}
<div id="example">
  {{ message }}
</div>
new Vue({
  el: '#example',
  data: object
})

This is now reactive!

Reactivity - how does it work?

Vue.js – Basics

When passing an object to the Vue instance as it’s data property, Vue.js walks through all it’s properties and converts them to getter/setters  using Object.defineProperty.

For every directive / data binding in the template, there will be a corresponding watcher object. When a dependency’s setter is called, it triggers the watcher to re-evaluate, and in turn causes its associated directive to perform DOM updates.

Reactivity - how does it work

Vue.js – Basics

Vue.js – Basics

You can use it „jQuery style”. Just add the library from the CDN and you’re ready to go.

Simple to use

No need for JSX, Webpack / Browserify, TypeScript or silly React.createElement(). 

You can use it to enhance your old projects and keep using all your favorite tools: Slim, Sass, CoffeeScript or simply es5. Vue doesn’t care.

Vue.js – Basics

However if you decide to go for a full blown SPA, you get all the tools you need.

Going full SPA

Vue-cli

  • Webpack / Browerify
  • Hot reloading
  • Linting
  • Unit/e2e tests
  • CSS extraction

Vue ecosystem

  • Vue-router
  • Vuex
  • Vue-devtools
  • Vue-touch
  • Vue-validation
  • and more...

Vue.js – Basics

Single

File

Component

Vue.js – Basics

Single

File

Component

(with preprocessors!)

Vue.js – Basics

What’s with the scoped attribute?

The one liner that prevents leaks of CSS outside of components.

Vue.js – Basics

Try it out!

Similarities with AngularJS

Vue.js – Similarities with AngularJS

Directives in template

Vue.js – Similarities with AngularJS

<!-- Vue syntax above
     Angular 1.x syntax below -->

<h1 v-if="ok">{{ name }}</h1>
<h1 v-else>No</h1>
<h1 ng-if="ok">{{ name }}</h1>
<h1 ng-if="!ok">No</h1>

<input type="text" v-model="name">
<input type="text" ng-model="name">

<small v-show="!ok">{{ Not OK! | capitalize }}</small>
<small ng-show="!ok">{{ Not OK! | capitalize }}</small>

<a v-bind:href="url">Go back</a>
<a ng-href="{{ url }}">Go back</a>

<button v-on:click="doSomething">Do something</button>
<button ng-click="doSomething">Do something</button>

Directives in template 2

Vue.js – Similarities with AngularJS

<!-- Vue syntax above
     Angular 1.x syntax below -->

<div v-bind:class="{ 'class-a': isA, 'class-b': isB }"></div>
<div ng-class="{ 'class-a': isA, 'class-b': isB }"></div>

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
<ul id="example-1">
  <li ng-repeat="item in items">
    {{ item.message }}
  </li>
</ul>

Shorthands in Vue

Vue.js – Similarities with AngularJS

<!-- full syntax -->
<button v-bind:disabled="someDynamicCondition">Button</button>

<!-- shorthand -->
<button :disabled="someDynamicCondition">Button</button>

or

<!-- full syntax -->
<a v-on:click="doSomething"></a>

<!-- shorthand -->
<a @click="doSomething"></a>

Computed values

Vue.js – Features

<div id="example">
  a={{ a }}, b={{ b }}
</div>
var vm = new Vue({
  el: '#example',
  data: {
    a: 1
  },
  computed: {
    // a computed getter
    b: function () {
      // `this` points to the vm instance
      return this.a + 1
    }
  }
})

Computed setter

Vue.js – Features

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ..

// When calling vm.fullName = 'John Doe', the setter will 
// be invoked and vm.firstName and vm.lastName 
// will be updated accordingly.

Style bindings

Vue.js – Features

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

<!-- Works mostly the same as with classes -->

Additionally

When you use a CSS property that requires vendor prefixes in v-bind:style, for example transform, Vue.js will automatically detect and add appropriate prefixes to the applied styles.

Transitions

Vue.js – Features

<div v-if="show" transition="expand"></div>
/* always present */
.expand-transition {
  transition: all .3s ease;
  height: 30px;
  padding: 10px;
  background-color: #eee;
  overflow: hidden;
}

/* .expand-enter defines the starting state for entering */
/* .expand-leave defines the ending state for leaving */
.expand-enter, .expand-leave {
  height: 0;
  padding: 0 10px;
  opacity: 0;
}

Vue.js – Features

List rendering

var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ],
    object: {
      FirstName: 'John',
      LastName: 'Doe',
      Age: 30
    }
  }
})
<ul id="example-1">
  <li v-for="item in items">
    {{ $index }} – {{ item.message }}
  </li>
</ul

or 

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider"></li>
  </template>
</ul>

<!-- Where the <template> element 
won’t render -->

or

<div v-for="(key, val) in object">
  {{ key }} {{ val }}
</div>

Methods & Events handling

Vue.js – Features

var vm = new Vue({
  el: '#example',
  data: {
    name: 'Vue.js'
  },
  // define methods under the `methods` object
  methods: {
    greet: function (user, event) {
      // `this` inside methods point to the Vue instance
      alert('Hello ' + user + ' this is ' + this.name + '!')
      // `event` gives us access to original DOM event 
    }
  }
})

// you can invoke methods in JavaScript too
vm.greet('Developers') // -> 'Hello Developers this is Vue.js!'
<button @click="greet('Developers', $event)">Greet</button>

Event modifiers

Vue.js – Features

<!-- the click event's propagation will be stopped -->
<a v-on:click.stop="doThis"></a>

<!-- the submit event will no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- modifiers can be chained -->
<a @click.stop.prevent="doThat">

<!-- just the modifier -->
<form v-on:submit.prevent></form>

<!-- only trigger handler if event.target is the element itself -->
<!-- i.e. not from a child element -->
<div @click.self="doThat">...</div>

<!-- Support for key aliases as modifiers -->
<input @keyup.enter="submit">

<!-- List of aliases: enter, tab, delete ,esc, space, up, down, left, right; 
or create your own with Vue.directive('on').keyCodes.f1 = 112 -->

Components

Vue.js – Components

<div id="example">
  <my-component></my-component>
</div>
// define
var MyComponent = Vue.extend({
  template: '<div>A component!</div>'
})

// register globally
Vue.component('my-component', MyComponent)

// create a root instance
new Vue({
  el: '#example'
})
<!-- Which will render -->
<div id="example">
  <div>A component!</div>
</div>

Local registration

Vue.js – Components

var Child = Vue.extend({ /* ... */ })

var Parent = Vue.extend({
  template: '...',
  components: {
    // <my-component> will only be available in Parent's template
    'my-component': Child
  }
})

Props

Vue.js – Components

Vue.component('child', {
  // declare the props
  props: ['myProp'],
  // the prop can be used inside templates, and will also
  // be set as `this.msg`
  template: '<span>{{ myProp }}</span>'
})
<child my-prop="hello!"></child>
<!-- The prop will be passed as string -->

Remember: When using camelCased prop names as attributes, you need to use their kebab-case equivalents.

Dynamic props

Vue.js – Components

<div>
  <child :my-value="obj"></child>
</div>
var Parent = Vue.extend({
  template: '...',
  components: {
    Child
  },
  data: function () {
    return {
      obj: { name: 'Vue', ext: 'JS' }
    }
  }
})

This will pass the whole object as prop.

Prop validation

Vue.js – Components

Vue.component('example', {
  props: {
    // basic type check (`null` means accept any type)
    propA: Number,
    // a required string
    propB: {
      type: String,
      required: true
    },
    // a number with default value
    propC: {
      type: Number,
      default: 100
    },
    // object/array defaults should be returned from a
    // factory function
    propD: {
      type: Object,
      default: function () {
        return { msg: 'hello' }
      }}}}

Prop validation 2

Vue.js – Components

Vue.component('example', {
  props: {
    // indicate this prop expects a two-way binding. will
    // raise a warning if binding type does not match.
    propE: {
      twoWay: true
    },
    // custom validator function
    propF: {
      validator: function (value) {
        return value > 10
      }
    },
    // coerce function (new in 1.0.12)
    // cast the value before setting it on the component
    propG: {
      coerce: function (val) {
        return val + '' // cast the value to string
      }
    }
}

Slots

Vue.js – Components

<!-- Component template -->
<div>
  <h1>This is my component!</h1>
  <slot>
    This will only be displayed if there is no content
    to be distributed.
  </slot>
</div>

<!-- Parent template -->
<my-component>
  <p>This is some original content</p>
  <p>This is some more original content</p>
</my-component>

<!-- Rendered result -->
<div>
  <h1>This is my component!</h1>
  <p>This is some original content</p>
  <p>This is some more original content</p>
</div>

(like transclusion in angularJS)

Named slots

Vue.js – Components

<!-- Component template -->
<div>
  <slot name="one"></slot>
  <slot></slot>
  <slot name="two"></slot>
</div>

<!-- Parent template -->
<multi-insertion>
  <p slot="one">One</p>
  <p slot="two">Two</p>
  <p>Default A</p>
</multi-insertion>

<!-- Rendered result -->
<div>
  <p slot="one">One</p>
  <p>Default A</p>
  <p slot="two">Two</p>
</div>

Dynamic components

Vue.js – Components

new Vue({
  el: 'body',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
<component :is="currentView">
  <!-- component changes when vm.currentview changes! -->
</component>

Dynamic components 2

Vue.js – Components

<component :is="currentView" keep-alive>
  <!-- inactive components will be cached! -->
</component>
Vue.component('activate-example', {
  activate: function (done) {
    var self = this
    loadDataAsync(function (data) {
      self.someData = data
      done()
    })
  }
})

keep-alive

activate hook

Runs each time a component is dynamicaly swaped and at time of the inital render

transition-mode:

in-out

out-in

new in, then old out

old out, then new in

Compilation scope

Vue.js – Components

<!-- {{ msg }} will be compiled 
using the parent’s scope -->

<child-component>
  {{ msg }}
</child-component>

Everything in the parent template is compiled in parent scope; everything in the child template is compiled in child scope.

Mixins

Vue.js – Mixins

// define a mixin object
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// define a component that uses this mixin
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // -> "hello from mixin!"

Mixing mixins 

Vue.js – Mixins

var mixin = {
  created: function () {
    console.log('mixin hook called')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('component hook called')
  }
})

// -> "mixin hook called"
// -> "component hook called"

Mixing mixins 

Vue.js – Mixins

var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}
var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})
vm.foo() // -> "foo"
vm.bar() // -> "bar"
vm.conflicting() // -> "from self"

Vue-Router

Vue.js – Plugins

import Vue from 'vue'
import Router from 'vue-router'
import App from './app.vue'
import ViewA from './view-a.vue'
import ViewB from './view-b.vue'

Vue.use(Router)

const router = new Router()

router.map({
  '/a': { component: ViewA },
  '/b': { component: ViewB }
})

router.start(App, '#app')
<div>
  <h1>This is the layout that won't change</h1>
  <router-view><!-- matched component renders here --></router-view>
</div>

Vuex

Vue.js – Plugins

import Vuex from 'vuex'

const state = {
  counter: 0
}

const mutations = {
  INCREMENT (state) {
    state.counter++
  }
}

export default new Vuex.Store({
  state,
  mutations
})
store.dispatch('INCREMENT')

console.log(store.state.count) // -> 1

Vue.js – Plugins

Example flow

Vue.js – Plugins

Vue-devtools

Vue.js – Plugins

Vue.js vs Angular 1.x

Vue.js – Mixins

Simpler API and design. 

 

Less opinionated, easier to combine with other libraries.

 

One-way data binding. Supports explicit two-way bindings.

 

Better performance and easier to optimize.

Complex API, hard to master.

 

Very opinionated, requires doing things angular-way.

 

Two-way bindings. Explicit one-way bindings (1.3+).

 

Dirty-checking. Nuff said.

Differences:

Vue.js vs Angular 1.x

Vue.js – Mixins

Components and directives

 

Template bindings (ng-if, ng-repeat vs v-if, v-for)

 

Concepts naming i.e. scope, watcher, directive, filters

Similarities:

Vue.js vs React

Vue.js – Mixins

DOM with references to real nodes.

 

Clean separation of HTML & JS. Easier to visually think about designs.

 

Prefers mutating state

Virtual DOM, rerender and patch DOM on every update.

 

Puts HTML inside JS. Easy to leak lots of logic inside render function.

 

Prefers immutable state

Differences:

Vue.js vs React

Vue.js – Mixins

Works on real DOM

 

 

Easy support for popular preprocessors for HTML, JS, CSS

 

Friendly for newcomers and easier to get productive fast

 

Actually more fun!

Tricky to perform DOM 

manipulations

 

More Everything-in-JS approach

 

Due to functional nature, steeper learning curve

 

More platform-agnostic

Differences:

Vue.js vs React

Vue.js – Mixins

Reactive and based on components

 

Just the View layer. Support for different state management solutions e.g. Flux/Redux. Vue also introduces Vuex and bindings for Redux (like revue)

 

Performant!

Similarities:

Growing popularity

Vue.js

Some stats (2015)

Vue.js

General

  • NPM downloads: 382,184 ytd, ~52k/month current      
  • GitHub Stars: 11,357 current

Repo Activity

  • Releases: 54 ytd (from 0.11.5 to 1.0.12, including alpha/beta/rc releases)

  • Commits: 1,023 ytd

  • Issues Closed: 1,014 ytd

  • Pull Requests Merged: 69 ytd from 43 contributors

Vuejs.org

  • Page views: 3,761,728 ytd                                                   

  • Unique visitors: 363,365 ytd     

  • 30 Day Active Users: 76,090 current                                 

Vue.js

Yes, this is a personal project. So if you are looking for an enterprise backed dev team, Vue is probably not the one. But I’d rather look at the numbers. Vue has maintained 100% test coverage on every single commit since the 0.11 rewrite, and that will continue. GitHub issues are closed within an average of 13 hours, and there has been 1400+ of them. As of this writing, there are literally zero open and reproducible bugs.

Vue.js

By JManuel Ruiz

Vue.js

Quick Tour on Vue.js

  • 516