Enjoy the Vue.js
@andywoodme
<html> <head> <title>My Fab App</title> </head> <body> <my-fabulous-app></my-fabulous-app> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>My Fab App</title> <link rel="stylesheet" href="style.css"> </head> <body> <div id="app"> <my-fabulous-app></my-fabulous-app> </div> <script src="app.js"></script> </body> </html>
Vue.js can do this*
Vue.js 2.0
Progressive Web Framework by Evan You
Less More
React Vue Angular Ember MeteorTemplating!Engines
Backbone
<body> <div id="app"> {{ name }} </div> <script src="vue.js"></script> <script> var app = new Vue({ el: '#app', data: { name: 'Hello!' } }) </script> </body>
index.html
DOM (Page)
Data (State)
Reactive
<body> <div id="app"> <input v-model="name"> {{ name }} </div> <script src="vue.js"></script> <script> var app = new Vue({ el: '#app', data: { name: 'Hello!' } }) </script> </body>
index.html
<div id="app"> <select v-model="type"> <option value="icon-dice">Board Game</option> <option value="icon-spades">Card Game</option> </select> <input v-model="name"> <p> <span :class="type"></span> {{ name }} </p> </div>
index.html
! data: { name: 'Snakes and Ladders', type: 'icon-dice' }
One-way text interpolation !{{ message }} !!One-way binding !<option v-bind:selected="value">...</option> !!Two-way binding !<input v-model="message">
</select> <input v-model="game"> <button @click="games.push({name: name, type: type})”>Add</button> ! <p v-if="games.length == 0">Zarro Boords!</p> <ul v-else> <li v-for="game in games"> <span :class="game.type"></span> {{ game.name }} </li> </ul> </div>
index.html
! data: { name: 'Snakes and Ladders', type: 'icon-dice', games: [] }
<input v-model="name"> <button @click="games.push({name: name, type: type})">Add</button> <p v-if="empty">Zarro Boords!</p> <ul v-else> <li v-for="game in games"> !
index.html
! data: { name: '', type: 'dice', games: [] }, computed: { empty: function() { return this.games.length == 0 } }
</select> <input v-model="name" @keyup.enter="addGame"> <button @click="addGame">Add</button> <p v-if="empty">Zarro Boords!</p> <ul v-else> !
index.html
computed: { empty: function() { return this.games.length == 0 } }, methods: { addGame: function () { this.games.push({ name: this.name, type: this.type }) } }
<select v-model="type"> <option value="dice">Board Game</option> <option value="spades">Card Game</option> </select> : : <span :class="icon(game.type)"></span>
index.html
computed: { empty: function() { return this.games.length == 0 } }, methods: { icon: function (type) { return 'icon-' + type }, addGame: function () { this.games.push({
<div id="app"> <select v-model="type"> <option value="dice">Board Game</option> <option value="spades">Card Game</option> </select> <input v-model="name" @keyup.enter="addGame"> <button @click="addGame">Add</button> <p v-if="empty">Zarro Boords!</p> <ul v-else> <li v-for="game in games"> <span :class="icon(game.type)"></span> {{ game.name }} </li> </ul> </div>
index.html
<script> var app = new Vue({ el: '#app', data: { name: '', type: 'dice', games: [] }, computed: { empty: function() { return this.games.length == 0 } }, methods: { addGame: function () { this.games.push({ name: this.name, type: this.type }) }, icon: function (type) { return 'icon-' + type } } }) </script>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>My Fab App</title> <link rel="stylesheet" href="style.css"> </head> <body> <div id="app"> <my-fabulous-app></my-fabulous-app> </div> <script src="app.js"></script> </body> </html>
Components
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>iBoards</title> <link rel="stylesheet" href="css/fonts.css"> </head> <body> <div id="app"> <board-library></board-library> </div> <script src="dist/app.js"></script> </body> </html>
index.html
var Vue = require('vue') var BoardLibrary = require('./BoardLibrary.vue') !Vue.component('board-library', BoardLibrary) var app = new Vue({ el: '#app' })
main.js
<template> <div> <select v-model="type"> <option value="dice">Board Game</option> : </div> </template> !<script> module.exports = { data: function() { return { game: '', type: 'dice', games: [] : </script>
BoardLibrary.vue
!// dependencies var Vue = require('vue') !// module implementation function myFunc() { : } !// exported module.exports = { myFunc: myFunc }
CommonJS
Node / Server Side
BoardLibrary.vue
main.js
app.js
npm install -g vue-cli vue init browserify-simple my-project !Using grunt or gulp already? vueify or vue-loader slot straight in
vue.js
Components
Component
Sub-components
Properties
:data="value"
props: ['data']
<p v-if="empty">Zarro Boords!</p> <ul v-else> <board-game v-for="game in games" :type="game.type" :name="game.name"></board-game> </ul>
BoardLibrary.vue
<script> var BoardGame = require('./BoardGame.vue') !module.exports = { data: function() { : components: { BoardGame: BoardGame } } </script>
<template> <li> <span :class="icon"></span> {{ name }} </li> </template> !<script> module.exports = { props: [ 'name', 'type' ], computed: { icon: function () { return 'icon-' + this.type } } } </script>
BoardGame.vue
<ul> <board-game v-for="game in games" :type="game.type" :name=“game.name"></board-game> </ul>
BoardLibrary.vue
! props: ['name', ‘type’], computed: { icon: ...
BoardGame.vue
<li> <span :class="icon"></span> {{ name }} </li>
! data: { games: [ { name: 'Carcasonne', type: 'dice' }, { name: 'Linkee', type: 'spade' } ] }
kebab
camel
Events
props: ['data']
@event="handler"
this.$emit('event')
:data="value"
<template> <div> <board-add @add="handleAdd"></board-add> <p v-if="empty">Zarro Boords!</p> <ul v-else> <board-game v-for="game in games" :type="game.type" :name="game.name"></board-game> </ul> </div> </template> !<script> var BoardAdd = require('./BoardAdd.vue') var BoardGame = require('./BoardGame.vue') !module.exports = { data: function() { return { games: [] } }, : methods: { handleAdd: function (game) { this.games.push(game) } }, components: { BoardAdd: BoardAdd, BoardGame: BoardGame } } </script>
BoardLibrary.vue
<template> <div> <select v-model="type"> <option value="dice">Board Game</option> <option value="spades">Card Game</option> </select> <input v-model="name" @keyup.enter="add"> <button @click="add">Add</button> </div> </template> !<script> module.exports = { data: function() { return { name: '', type: 'dice', } }, methods: { add: function () { this.$emit('add', { name: this.name, type: this.type }) } } } </script>
BoardAdd.vue
<template> <span class="icon-bin" @click="del"></span> </template> !<script> module.exports = { props: [ 'index' ], methods: { del: function () { this.$emit('delete', this.index) } } } </script>
BoardDelete.vue
<board-game v-for="(game, index) in games" :type="game.type" :name="game.name"> <board-delete :index="index" @delete="handleDelete"></board-delete> </board-game>
BoardLibrary.vue
methods: { handleAdd: function (game) { this.games.push(game) }, handleDelete: function (index) { this.games.splice(index, 1) } }, components: { BoardDelete: BoardDelete,
<template> <tr> <td><span :class="icon"></span></td> <td>{{ name }}</td> <td><slot></slot></td> </tr> </template>
BoardGame.vue
<table v-else> <thead> <tr> <board-sort v-model="sortCol" col="type">Type</board-sort> <board-sort v-model="sortCol" col="name">Name</board-sort> <th></th> </tr> </thead> <tbody> <board-game v-for="game in sorted" :type="game.type" :name="game.name"> <board-delete :index="game.index" @delete="handleDelete"></board-delete> </board-game> </tbody> </table>
BoardLibrary.vue
BoardLibrary.vue data: function() { return { sortCol: 'type', games: [] } }, computed: { sorted: function() { // update each game with its index in the array this.games.forEach(function (value, index) { value.index = index }) return _.sortBy(this.games, [this.sortCol]) }, :
! _.sortBy(this.games, [this.sortCol]) ![ { name: 'Codenames', type: 'Card', index: 2 }, { name: 'Snake Oil', type: ‘Card', index: 0 }, { name: 'Star Wars', type: 'Board', index: 1 } ]
games: [ { name: 'Snake Oil', type: 'Card' }, { name: 'Star Wars', type: 'Board' }, { name: 'Codenames', type: 'Card' } ], sortCol: 'name'
sorted()
data
<board-game v-for="game in sorted" ...>
<button class="button-primary" :disabled="blankName" @click="add">Add</button>
BoardAdd.vue
computed: { blankName: function() { return this.name.trim().length == 0 } }, methods: { add: function () { if (!this.blankName) { this.$emit('add', { name: this.name.trim(), type: this.type }) this.name = '' } } }, mounted: function() { this.$refs.name.focus() }
BoardAdd.vue} </script> !<style scoped> input, select { width: 100%; } button { margin-top: 2.9rem; } button[disabled] { opacity: 0.5; } button[disabled]:hover { background-color: #33C3F0; border-color: #33C3F0; } </style>
1. It’s Approachable
Alice Bartlett Origami Components Lead, FT
2. Single File Components
concern
concern
concern
concern
concern
concern
concern
concern
concern
concern
3. Incremental Adoptability
more Library-ish than Framework-esque
Vue Resource !
Vue Router !
Vuex
AJAXy GET/POSTs !
Single Page App URLs !
State Machine
4. Plays well with Others
<script lang="coffee"> module.exports = props: ['options'] methods: sort: (event) -> @$emit ‘change' event.target.value </script> !<style lang="sass"> @import "components"; div.dashboard-sort { margin-right: 1.45em; margin-bottom: 1em; @include select(1.25em); } </style>
5. Goes with the Grain
One of Ruby’s friends really makes her crazy. When they try to wrap Christmas presents together Ruby wants to be creative and have many different ways of using the wrapping paper. !- No, says Django.
There is one - and only one - obvious way to wrap a present.
Gotchas
google vue events !
use of this. !
array manipulations
</thead> <transition-group name="board-list" tag="tbody"> <board-game v-for="game in sorted" :type="game.type" :name="game.name" :key="game.id"> <board-delete :id="game.id" @delete="handleDelete"></board-delete> </board-game> </transition-group> </table>
BoardLibrary.vue
methods: { handleAdd: function (game) { game.id = idGenerator++ this.games.push(game) },
<style> .board-list-move { transition: transform 1s; } .board-list-enter-active, .board-list-leave-active { transition: all 0.5s; } .board-list-enter, .board-list-leave-active { opacity: 0; } .board-list-enter { transform: translateX(960px); } .board-list-leave-active { transform: translateX(-960px); } :
BoardLibrary.vue
Thank you
@andywoodme
CreditsDoge / Minecraft - http://www.yoyowall.com/wallpaper/dog-mountains.html LED Lightbulb - http://edisonlightglobes.com/ Bricksauria T.rex - https://www.flickr.com/photos/115291125@N07/12107675253/ CSS Windows - https://twitter.com/neonick/status/747423962783748096 DJECO - http://www.djeco.com/en !JS Framework Spectrum - The Progressive Framework, Evan You Documentation isn’t Complicated - Can't you make it more like Bootstrap, Alice Bartlett Only one obvious way to wrap a present - Ruby & Django, Linda Liukas !Further Reading !Vue.js - https://vuejs.org Vue Awesome - https://github.com/vuejs/awesome-vue How popular is Vue.js - https://www.quora.com/How-popular-is-VueJS-in-the-industry iBoards code examples - https://github.com/woodcoder/vue-2-list-example
AMD — RequireJS / client side ! define(['vue'], function (Vue) { …
UMD — two-in-one ! (function (root, factory) { if (typeof define === 'function' && define.amd) { …
ES2015 / ES6 — tree shakeable ! import Vue from ‘vue’ ! export default {...
XKCD 927 — standards