A view on Vue

(last pun, I promise!)

Who am I?

  • Vue.js core team
  • Web UI/visualization developer and consultant

@chrisvfritz     (slack.lansing.codes)

@chrisvfritz

@chrisvfritz 

What are components?

HTML

  • todo-list.html
  • _todo.html
  • _button.html

 

CSS

  • todo-list.css

 

JS

  • todo-list.js

TodoList

HTML

CSS

JS

 

Todo

HTML

CSS

JS

 

Button

HTML

CSS

JS

Components are the future

...but which component system?

"My biggest frustration is the constant migration from tool to tool or framework to framework. Never any time to master one before something better comes along."

The elephant in the room...

What would it take?

  1. Be productive almost immediately
  2. Simple (in practice!) component model
  3. Fast, easy-to-optimize rendering
  4. Must scale well to large apps
  5. Top-class dev workflow
  6. Stable and rich ecosystem

Coming from React, this is what I needed

Getting started

  • Documentation
  • Learning curve
  • Support

Documentation

Learning curve

<div id="app">
  <input v-model="greeting">
  <p>{{ greeting }} World</p>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
  new Vue({
    el: '#app',

    data: {
      greeting: 'Hello'
    }
  })
</script>

Hello world!

Angular

Learning curve

<script src="https://unpkg.com/angular/angular.js"></script>
<script>
  angular.module('HelloWorldApp', [])
    .controller('HelloWorldController', function ($scope) {
      $scope.greeting = 'Hello'
    })
</script>

<div ng-app="HelloWorldApp">
  <div ng-controller="HelloWorldController">
    <input ng-model="greeting">
    <p>{{ greeting }} world</p>
  </div>
</div>

and with Angular 1...

Learning curve

<div id="app">
  <input v-model="newTodoText" @keydown.enter="addTodo">
  <ul>
    <li v-for="(todo, index) in todos">
      {{ todo.text }}
      <button @click="removeTodo(index)">X</button>
    </li>
  </ul>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
  new Vue({
    el: '#app',

    data: {
      newTodoText: '',
      todos: []
    },

    methods: {
      addTodo: function () {
        this.todos.push({ text: this.newTodoText })
        this.newTodoText = ''
      },
      removeTodo: function (index) {
        this.todos.splice(index, 1)
      }
    }
  })
</script>

Simple todo app

Angular

Learning curve

<script src="https://unpkg.com/angular/angular.js"></script>
<script>
  angular.module('TodoApp', [])
    .controller('TodoController', function ($scope) {
      $scope.newTodoText = ''
      $scope.todos = []

      $scope.addTodo = function () {
        $scope.todos.push({ text: $scope.newTodoText })
        $scope.newTodoText = ''
      }

      $scope.removeTodo = function (index) {
        $scope.todos.splice(index, 1)
      }
    })
</script>

<div ng-app="TodoApp">
  <div ng-controller="TodoController">
    <input ng-model="newTodoText" ng-keydown="$event.which === 13 && addTodo()">
    <ul>
      <li ng-repeat="todo in todos">
        {{ todo.text }}
        <button ng-click="removeTodo($index)">X</button>
      </li>
    </ul>
  </div>
</div>

and with Angular 1...

Learning curve

<div id="app">
  It will have been 2 minutes since I loaded this page in
  <auto-counter
    :initial-value="120"
    :increment-by="-1"
    :stop-at="0"
  ></auto-counter>
  seconds.
</div>

<script src="https://unpkg.com/vue"></script>
<script>
  Vue.component('auto-counter', {
    template: '<span>{{ number }}</span>',

    props: {
      initialValue: {
        type: Number,
        default: 0
      },
      incrementBy: {
        type: Number,
        default: 1
      },
      interval: {
        type: Number,
        default: 1000
      },
      stopAt: {
        type: Number
      }
    },

    data: function () {
      return {
        number: this.initialValue
      }
    },

    created: function () {
      var vm = this
      vm.counter = setInterval(function () {
        vm.number += vm.incrementBy
        if (this.stopAt === vm.number) {
          clearInterval(vm.counter)
        }
      }, vm.interval)
    },

    beforeDestroy: function () {
      clearInterval(this.counter)
    }
  })

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

Reusable components

Angular

Learning curve

<script src="https://unpkg.com/angular/angular.js"></script>
<script>
  angular.module('App', [])
    .component('autoCounter', {
      template: '<span>{{ $ctrl.number }}</span>',

      controller: function ($scope) {
        var vm = this
        
        vm.$onInit = function () {
          vm.number = vm.initialValue || 0
          vm.incrementBy = vm.incrementBy || 1
          vm.interval = vm.interval || 1000
          
          vm.counter = setInterval(function () {
            $scope.$apply(function () {
              vm.number += vm.incrementBy
              if (vm.stopAt === vm.number) {
                clearInterval(vm.counter)
              }
            })
          }, vm.interval)
        }
       
       	vm.$onDestroy = function () {
        	clearInterval(vm.counter)
        }
      },

      bindings: {
        initialValue: '<',
        incrementBy: '<',
        interval: '<',
        stopAt: '<'
      }
    })
</script>

<div ng-app="App">
  It will have been 2 minutes since I loaded this page in
  <auto-counter
    initial-value="120"
    increment-by="-1"
    stop-at="0"
  ></auto-counter>
  seconds.
</div>

and with Angular 1...

Support

  • GitHub
  • Discourse forum
  • Gitter chat

Single-File Components

Build on web standards, don't reinvent them

<template>
  <button :class="'btn btn-' + kind">
    <slot></slot>
  </button>
</template>

<script>
export default {
  props: {
    kind: {
      validator (value) {
        return ['primary', 'warning'].indexOf(value) !== -1
      },
      required: true
    }
  }
}
</script>

<style lang="scss" scoped>
$text-color: #fff;
$primary-bg-color: #0074d9;
$warning-bg-color: #d5a13c;

.btn {
  color: $text-color;

  &.btn-primary {
    background: $primary-bg-color;

    &:hover {
      background: lighten($primary-bg-color, 20%);
    }
  }

  &.btn-warning {
    background: $warning-bg-color;

    &:hover {
      background: lighten($warning-bg-color, 20%);
    }
  }
}
</style>

Integrating 3rd Party Libs

<template>
  <textarea :value="markdown" @input="updateMarkdown"></textarea>
  <div v-html="preview"/>
</template>

<script>
import debounce from 'lodash/debounce'
import compileMarkdown from 'marked'

export default {
  el: '#editor',
  data: {
    markdown: '# hello'
  },
  computed: {
    preview: function () {
      return compileMarkdown(this.markdown)
    }
  },
  methods: {
    updateMarkdown: debounce(function (event) {
      this.markdown = event.target.value
    }, 300)
  }
})
</script>

Markdown Editor

Integrating 3rd Party Libs

Visualization with D3

I need to create a page that displays the latest activity from the users, so I just need to get the data from the REST endpoint and display it in some sort of filterable table, and update it if anything changes in the server.

Integrating 3rd Party Libs

REST

Scaling Up

Dev Workflow

In-browser devtools

Dev Workflow

With time travel!

Dev Workflow

Hot module reloading

Ecosystem

Is anyone actually using it in production?

Ecosystem

​Official support for practically required libs (like for routing and state management)

github.com/vuejs/vue-resource

github.com/vuejs/vue-router

github.com/vuejs/vuex

github.com/vuejs/vue-cli

github.com/vuejs/vue-devtools

github.com/vuejs/vue-rx

...

Ajax

Routing

Flux State

CLI Generator

Devtools

Observables

...

Plumbing

Give it a day

(seriously!)

 

vuejs.org/guide

@chrisvfritz     (slack.lansing.codes)

@chrisvfritz

@chrisvfritz 

slides.com/chrisfritz/vue-mmcfug

Intro to Vue.js

By Chris Fritz

Intro to Vue.js

  • 1,885