SlideShare a Scribd company logo
Vue.js
Hello!Javier Lafora
CTO at ASPgems
@eLafo
At the moment of preparing
this talk the latest version
of vue is 2.3
What are we going to talk about?
Why?
Reasons to use a JS
frontend framework
× UI Complexity
× Rapid Prototyping
× JS Spaguetti
What?
What Vue.js is
× Declarative
× MVVM
× Reactivity
× Virtual DOM
× Components
How?
How to build a Vue.js
project
× Components
identification
× model tree design
× Development
× Setup Vue
× Components
× Dev tools
What are we NOT going to talk about?
ES2015
Webpack
JS internals
Testing
Deploy
Authorization
Plugins
Routing
Middleware
Reactivity internals
Persistence
Server Side
Rendering
Vuex
XSS or CORS
Connecting to third
party services
Components
lifecycle
Transitions
Animations
1.
Why?
3 reasons why you would want to
use a proper front-end framework
UI Complexity
Web based apps UI have increased their
complexity, and they do not longer behave as
traditional web sites but as (browser) native
apps.
An introduction to Vue.js
An introduction to Vue.js
Rapid prototyping
HTML, CSS & JS are the new photoshop. You
want to prototype not only visual aspects of
your apps, but your app’s UX. You need to do
this as fast as possible… and you do not want to
be the bottleneck.
JS / JQUERY Spaguetti
Recipe:
1. Forget about separation of concerns
2. Mix structure, presentation and data logic
together.
3. Throw this mixture in the DOM
4. Enjoy your food
PS: Do not consume if you like sleeping
2.
What?
Basic VueJS concepts
Declarative
Tell what to render
instead of how
An introduction to Vue.js
MVVM
Reactive Data Binding
Virtual DOM
Components
Components are one of the most powerful
features of Vue. WIth them you can build
reusable features to be used easily -thanks to
its declarative nature- by other developers.
Components concerns
× Structure -> <Template>
Components concerns
× Structure -> <Template>
× Presentation -> <Style>
Components concerns
× Structure -> <Template>
× Presentation -> <Styles>
× Data -> <Script>
<Template>
Managing information
structure
Directives
Directives & attributes
Special attributes
Directives
v-text (use handlebars {})
v-on (shorthand @)
v-bind (shorthand :)
v-show
v-if / v-else / v-else-if /
v-for / key
slot
v-model
v-pre / v-cloak / v-once
Directives & attributes
Special attributes
Directives
v-text (use handlebars {})
v-on (shorthand @)
v-bind (shorthand :)
v-show
v-if / v-else / v-else-if /
v-for / key
slot
v-model
v-pre / v-cloak / v-once
Directives & attributes
Special attributes
key
ref
slot
is
<SCRIPT>
Managing data
Component internals
Component internals
× data: Internal state
Component internals
× data: Internal state
× props: args
Component internals
× data: Internal state
× props: args
× computed: Cacheable read functions
Component internals
× data: Internal state
× props: args
× computed: Cacheable read functions
× methods: Event handling
Component internals
× data: Internal state
× props: args
× computed: Cacheable read functions
× methods: Event handling
× Provided by plugins: Extensions
Vuex
Vuex
× state: application state as a tree
Vuex
× state: application state as a tree
× getters: state read methods
Vuex
× state: application state as a tree
× getters: state read methods
× mutations: state write methods
Vuex
× state: application state as a tree
× getters: state read methods
× mutations: state write methods
× actions: user commands
Vuerouter + vuex-router-sync
× state.route
× path
× params
× query
<style>
presenting information
An introduction to Vue.js
3.
HOW?
Building a VueJS application
My mental process is top-down
Component
identification
Model tree
design
Development
Place your screenshot here
Memo-vue
Let’s use this example from
https://github.com/akifo/vue-memo
Identifying
components
Identifying
components
× AppHeader
MAIN CONTENT
Identifying
components
× AppHeader
× [Main content]
Place your screenshot here
Identifying
components
× AppHeader
× [Main content]
× Index
Place your screenshot here
Identifying
components
× AppHeader
× [Main content]
× Index
× Memo
Place your screenshot here
Identifying
components
× AppHeader
× [Main content]
× Index
× Memo
× Viewer
Place your screenshot here
Identifying
components
× AppHeader
× [Main content]
× Index
× Memo
× Viewer
× Editor
Modeling
× State
State
{
// route: {} // vue-router has created state.route
}
State
{
user: {},
memos: {}
// route: {} // vue-router has created state.route
}
State
{
user: {
loggedIn: false
},
memos: {}
// route: {} // vue-router has created state.route
}
State
{
user: {
loggedIn: false,
uid: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
State
{
user: {
loggedIn: false,
uid: '',
name: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
State
{
user: {
loggedIn: false,
uid: '',
name: '',
profilePicUrl: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
State
{
user: {
loggedIn: false,
uid: '',
name: '',
profilePicUrl: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
memo = {
title: '',
body: ''
}
Modeling
× State
× getters
getters
Memos User
getters
Memos
× memos
User
getters
Memos
× memos
× currentMemo
User
getters
Memos
× memos
× currentMemo
× currentMemoId
User
getters
Memos
× memos
× currentMemo
× currentMemoId
User
× user
getters
Memos
× memos
× currentMemo
× currentMemoId
User
× user
× currentUserName
getters
Memos
× memos
× currentMemo
× currentMemoId
User
× user
× currentUserName
× currentUserId
Modeling
× State
× Getters
× mutations
Mutations
Memos User
Mutations
Memos
× setMemo
User
Mutations
Memos
× setMemo
× setMemos
User
Mutations
Memos
× setMemo
× setMemos
× deleteMemo
User
Mutations
Memos
× setMemo
× setMemos
× deleteMemo
User
× oAuthStateChanged
Mutations
Memos
× setMemo
× setMemos
× deleteMemo
User
× oAuthStateChanged
× setUser
Mutations
Memos
× setMemo
× setMemos
× deleteMemo
User
× oAuthStateChanged
× setUser
Modeling
× State
× Getters
× mutations
× Actions
Actions
User
× onAuthStateChanged
Memos
Actions
User
× onAuthStateChanged
× signIn
Memos
Actions
User
× onAuthStateChanged
× signIn
× signOut
Memos
Actions
User
× onAuthStateChanged
× signIn
× signOut
× setUserInfo
Memos
Actions
User
× onAuthStateChanged
× signIn
× signOut
× setUserInfo
Memos
× fetchMemos
Actions
User
× onAuthStateChanged
× signIn
× signOut
× setUserInfo
Memos
× fetchMemos
× fetchMemo
Actions
User
× onAuthStateChanged
× signIn
× signOut
× setUserInfo
Memos
× fetchMemos
× fetchMemo
× addMemo
Actions
User
× onAuthStateChanged
× signIn
× signOut
× setUserInfo
Memos
× fetchMemos
× fetchMemo
× addMemo
× deleteMemo
Actions
User
× onAuthStateChanged
× signIn
× signOut
× setUserInfo
Memos
× fetchMemos
× fetchMemo
× addMemo
× deleteMemo
× updateMemo
Development
Development
× Setup vue
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/main.js
import Vue from 'vue'
import App from './App'
import { sync } from 'vuex-router-sync'
import store from './vuex'
import router from './router'
sync(store, router)
const app = new Vue({
router,
store,
el: '#app',
render: h => h(App)
})
global._App = app
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/main.js
import Vue from 'vue'
import App from './App'
import { sync } from 'vuex-router-sync'
import store from './vuex'
import router from './router'
sync(store, router)
const app = new Vue({
router,
store,
el: '#app',
render: h => h(App)
})
global._App = app
Imports
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/main.js
import Vue from 'vue'
import App from './App'
import { sync } from 'vuex-router-sync'
import store from './vuex'
import router from './router'
sync(store, router)
const app = new Vue({
router,
store,
el: '#app',
render: h => h(App)
})
global._App = app
Imports
Includes current route in state
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/main.js
import Vue from 'vue'
import App from './App'
import { sync } from 'vuex-router-sync'
import store from './vuex'
import router from './router'
sync(store, router)
const app = new Vue({
router,
store,
el: '#app',
render: h => h(App)
})
global._App = app
Imports
Includes current route in state
Create Vue instance
Development
× Setup vue
× Store
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/vuex/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
user: {
loggedIn: false,
uid: '',
name: '',
profilePicUrl: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
const mutations = {// here come the mutations}
const actions = {// here come the actions}
const getters = {// here come the actions}
export default new Vuex.Store({ state, getters, actions, mutations, strict: debug })
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/vuex/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
user: {
loggedIn: false,
uid: '',
name: '',
profilePicUrl: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
const mutations = {// here come the mutations}
const actions = {// here come the actions}
const getters = {// here come the actions}
export default new Vuex.Store({ state, getters, actions, mutations, strict: debug })
Imports
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/vuex/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
user: {
loggedIn: false,
uid: '',
name: '',
profilePicUrl: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
const mutations = {// here come the mutations}
const actions = {// here come the actions}
const getters = {// here come the actions}
export default new Vuex.Store({ state, getters, actions, mutations, strict: debug })
Imports
Use it!!!
// This content has been reduced. You can find the original content in
https://github.com/akifo/vue-memo/blob/dev/src/js/vuex/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
user: {
loggedIn: false,
uid: '',
name: '',
profilePicUrl: ''
},
memos: {}
// route: {} // vue-router has created state.route
}
const mutations = {// here come the mutations}
const actions = {// here come the actions}
const getters = {// here come the actions}
export default new Vuex.Store({ state, getters, actions, mutations, strict: debug })
Imports
Use it!!!
Initial state
const mutations = {
onAuthStateChanged (state, { user }) {
state.user = user
},
setUser (state, { key, val }) {
Vue.set(state.user, key, val)
},
setMemo (state, { key, memo }) {
Vue.set(state.memos, key, memo)
},
setMemos (state, { memos }) {
state.memos = memos || {}
},
deleteMemo (state, { key }) {
Vue.delete(state.memos, key)
}
}
fetchMemo ({ commit, state }) {
Firebase.fetchMemo(state.route.params.id)
.then(obj => {
commit('setMemo', {
key: obj.key,
memo: obj.memo
})
})
},
fetchMemos ({ commit }, { count, type }) {
if (state.user.loggedIn || type === 'public') { // is signed
in.
Firebase.fetchMemos(count, type)
.then(memos => {
commit('setMemos', { memos })
})
} else { // is signed out. Localstrage
}
},
deleteMemo ({ commit, state }) {
return new Promise((resolve, reject) => {
if (state.user.loggedIn) { // is signed in. Firebase
Firebase.deleteMemo(state.route.params.id)
.then(key => {
commit('deleteMemo', { key })
resolve()
}).catch(reject)
} else { // is signed out. Localstrage
reject('still dev for guest')
}
})
const actions = {
onAuthStateChanged ({ commit }, user) {
commit('onAuthStateChanged', { user })
},
signIn () {
Firebase.signIn()
},
signOut () {
Firebase.signOut()
},
setUserInfo ({ commit, state }, { key, val }) {
return new Promise((resolve, reject) => {
if (state.user.loggedIn) { // is signed in. Firebase
Firebase.setUserInfo(key, val)
.then(() => {
commit('setUser', { key, val })
resolve()
}).catch(reject)
} else { // is signed out. Localstrage
reject('still dev for guest')
}
})
}
// Truncated code
fetchMemo ({ commit, state }) {
Firebase.fetchMemo(state.route.params.id)
.then(obj => {
commit('setMemo', {
key: obj.key,
memo: obj.memo
})
})
},
fetchMemos ({ commit }, { count, type }) {
if (state.user.loggedIn || type === 'public') { // is signed
in.
Firebase.fetchMemos(count, type)
.then(memos => {
commit('setMemos', { memos })
})
} else { // is signed out. Localstrage
}
},
deleteMemo ({ commit, state }) {
return new Promise((resolve, reject) => {
if (state.user.loggedIn) { // is signed in. Firebase
Firebase.deleteMemo(state.route.params.id)
.then(key => {
commit('deleteMemo', { key })
resolve()
}).catch(reject)
} else { // is signed out. Localstrage
reject('still dev for guest')
}
})
const actions = {
onAuthStateChanged ({ commit }, user) {
commit('onAuthStateChanged', { user })
},
signIn () {
Firebase.signIn()
},
signOut () {
Firebase.signOut()
},
setUserInfo ({ commit, state }, { key, val }) {
return new Promise((resolve, reject) => {
if (state.user.loggedIn) { // is signed in. Firebase
Firebase.setUserInfo(key, val)
.then(() => {
commit('setUser', { key, val })
resolve()
}).catch(reject)
} else { // is signed out. Localstrage
reject('still dev for guest')
}
})
}
// Truncated code
const getters = {
memos: state => state.memos,
currentMemoID: ({ route }) => route.params.id,
currentMemo: state => {
return state.route.params.id
? state.memos[state.route.params.id]
: {}
},
user: state => state.user,
currentUserName: state => state.user.name,
currentUserId: state => state.user.uid
}
Development
× Setup vue
× Store
× Router
// https://github.com/akifo/vue-memo/blob/dev/src/js/router/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'
import Index from '../components/Index'
import Editor from '../components/Editor'
import Viewer from '../components/Viewer'
Vue.use(VueRouter)
var router = new VueRouter({
routes: [
{
path: '/',
name: 'index',
component: Index
}, {
path: '/editor',
name: 'newEditor',
component: Editor
}, {
path: '/editor/:id',
name: 'updateEditor',
component: Editor
}, {
path: '/viewer/:id',
name: 'viewer',
component: Viewer
}
]
})
export default router
// https://github.com/akifo/vue-memo/blob/dev/src/js/router/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'
import Index from '../components/Index'
import Editor from '../components/Editor'
import Viewer from '../components/Viewer'
Vue.use(VueRouter)
var router = new VueRouter({
routes: [
{
path: '/',
name: 'index',
component: Index
}, {
path: '/editor',
name: 'newEditor',
component: Editor
}, {
path: '/editor/:id',
name: 'updateEditor',
component: Editor
}, {
path: '/viewer/:id',
name: 'viewer',
component: Viewer
}
]
})
export default router
Imports
// https://github.com/akifo/vue-memo/blob/dev/src/js/router/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'
import Index from '../components/Index'
import Editor from '../components/Editor'
import Viewer from '../components/Viewer'
Vue.use(VueRouter)
var router = new VueRouter({
routes: [
{
path: '/',
name: 'index',
component: Index
}, {
path: '/editor',
name: 'newEditor',
component: Editor
}, {
path: '/editor/:id',
name: 'updateEditor',
component: Editor
}, {
path: '/viewer/:id',
name: 'viewer',
component: Viewer
}
]
})
export default router
Imports
Use it!!!
// https://github.com/akifo/vue-memo/blob/dev/src/js/router/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'
import Index from '../components/Index'
import Editor from '../components/Editor'
import Viewer from '../components/Viewer'
Vue.use(VueRouter)
var router = new VueRouter({
routes: [
{
path: '/',
name: 'index',
component: Index
}, {
path: '/editor',
name: 'newEditor',
component: Editor
}, {
path: '/editor/:id',
name: 'updateEditor',
component: Editor
}, {
path: '/viewer/:id',
name: 'viewer',
component: Viewer
}
]
})
export default router
Imports
Use it!!!
Your routes
Development
× Setup vue
× Store
× Router
× Root app component
// https://github.com/akifo/vue-memo/blob/dev/src/js/App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
</div>
</template>
<script>
import AppHeader from './components/AppHeader.vue'
export default {
components: { AppHeader },
created () {
this.$store.dispatch('fetchMemos', {
count: 10,
type: 'public'
})
}
}
</script>
<style lang="stylus">
body
margin: 0
padding: 0
</style>
// https://github.com/akifo/vue-memo/blob/dev/src/js/App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
</div>
</template>
<script>
import AppHeader from './components/AppHeader.vue'
export default {
components: { AppHeader },
created () {
this.$store.dispatch('fetchMemos', {
count: 10,
type: 'public'
})
}
}
</script>
<style lang="stylus">
body
margin: 0
padding: 0
</style>
Using components
// https://github.com/akifo/vue-memo/blob/dev/src/js/App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
</div>
</template>
<script>
import AppHeader from './components/AppHeader.vue'
export default {
components: { AppHeader },
created () {
this.$store.dispatch('fetchMemos', {
count: 10,
type: 'public'
})
}
}
</script>
<style lang="stylus">
body
margin: 0
padding: 0
</style>
Using components
Import subcomponents
// https://github.com/akifo/vue-memo/blob/dev/src/js/App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
</div>
</template>
<script>
import AppHeader from './components/AppHeader.vue'
export default {
components: { AppHeader },
created () {
this.$store.dispatch('fetchMemos', {
count: 10,
type: 'public'
})
}
}
</script>
<style lang="stylus">
body
margin: 0
padding: 0
</style>
Using components
Export subcomponents
Import subcomponents
// https://github.com/akifo/vue-memo/blob/dev/src/js/App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
</div>
</template>
<script>
import AppHeader from './components/AppHeader.vue'
export default {
components: { AppHeader },
created () {
this.$store.dispatch('fetchMemos', {
count: 10,
type: 'public'
})
}
}
</script>
<style lang="stylus">
body
margin: 0
padding: 0
</style>
Using components
Export subcomponents
Hook
Import subcomponents
Development
× Setup vue
× Store
× Router
× Root app component
× Create components
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<template>
<header class="appHeader navbar-fixed">
<nav>
<div class="nav-wrapper">
<h1 class="left">
<router-link :to="{ name: 'index' }">Vue-memo</router-link>
</h1>
<ul class="right">
<li>
<input type="text" :value="currentUserName" @input="update" @keyup.enter="submit">
</li>
<li v-if="!user.loggedIn" @click="signIn" class="googleBtnWrap">
<img class="normal" src="./../assets/btn_google_signin_light_normal_web@2x.png">
<img class="focus" src="./../assets/btn_google_signin_light_focus_web@2x.png">
<img class="pressed" src="./../assets/btn_google_signin_light_pressed_web@2x.png">
</li>
<li v-if="user.loggedIn"><a @click="signOut">signout</a></li>
</ul>
</div>
</nav>
</header>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<script>
import { mapGetters, mapActions } from 'vuex'
import _ from 'lodash'
export default {
name: 'AppHeader',
computed: mapGetters(['user', 'currentUserName']), // import easily getters from your store
data () { // data now stores internal component state, no application state e.g: active filters, active/inactive status, etc.
return {
name: ''
}
},
methods: {
...mapActions(['signIn', 'signOut']), // import easily actions from your store
update: _.debounce(function (e) {
this.submit(e)
}, 2000),
submit: _.throttle(function (e) {
// Here comes the implementation of this method
}, 800),
validate (text) {
// Here comes the implementation of this method
}
}
}
</script>
<style lang="stylus">
// Here comes some styling stuff
</style>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<script>
import { mapGetters, mapActions } from 'vuex'
import _ from 'lodash'
export default {
name: 'AppHeader',
computed: mapGetters(['user', 'currentUserName']), // import easily getters from your store
data () { // data now stores internal component state, no application state e.g: active filters, active/inactive status, etc.
return {
name: ''
}
},
methods: {
...mapActions(['signIn', 'signOut']), // import easily actions from your store
update: _.debounce(function (e) {
this.submit(e)
}, 2000),
submit: _.throttle(function (e) {
// Here comes the implementation of this method
}, 800),
validate (text) {
// Here comes the implementation of this method
}
}
}
</script>
<style lang="stylus">
// Here comes some styling stuff
</style>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/AppHeader.vue
<script>
import { mapGetters, mapActions } from 'vuex'
import _ from 'lodash'
export default {
name: 'AppHeader',
computed: mapGetters(['user', 'currentUserName']), // import easily getters from your store
data () { // data now stores internal component state, no application state e.g: active filters, active/inactive status, etc.
return {
name: ''
}
},
methods: {
...mapActions(['signIn', 'signOut']), // import easily actions from your store
update: _.debounce(function (e) {
this.submit(e)
}, 2000),
submit: _.throttle(function (e) {
// Here comes the implementation of this method
}, 800),
validate (text) {
// Here comes the implementation of this method
}
}
}
</script>
<style lang="stylus">
// Here comes some styling stuff
</style>
methods: {
...mapActions(['signIn', 'signOut']),
update: _.debounce(function (e) {
this.submit(e)
}, 2000),
submit: _.throttle(function (e) {
const name = e.target.value
this.validate(name)
.then(() => {
return this.$store.dispatch('setUserInfo', {
key: 'name',
val: name
})
}).then(() => {
Materialize.toast('Updated Name', 4000, 'green')
}).catch(err => {
Materialize.toast('Error ' + err, 4000, 'red')
})
}, 800),
validate (text) {
return new Promise((resolve, reject) => {
if (text.length < 3) reject('too short')
else if (text.length > 15)reject('too long')
else resolve()
})
}
}
}
methods: {
...mapActions(['signIn', 'signOut']),
update: _.debounce(function (e) {
this.submit(e)
}, 2000),
submit: _.throttle(function (e) {
const name = e.target.value
this.validate(name)
.then(() => {
return this.$store.dispatch('setUserInfo', {
key: 'name',
val: name
})
}).then(() => {
Materialize.toast('Updated Name', 4000, 'green')
}).catch(err => {
Materialize.toast('Error ' + err, 4000, 'red')
})
}, 800),
validate (text) {
return new Promise((resolve, reject) => {
if (text.length < 3) reject('too short')
else if (text.length > 15)reject('too long')
else resolve()
})
}
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/Index.vue
<template>
<div class="index container">
<div class="filter">
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'public' }"
@click="switchTimeLine('public')">Public</a>
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'myself' }"
@click="switchTimeLine('myself')">Myself</a>
</div>
<ul class="collection">
<memo v-for="(memo, key) in orderedMemos" :memo="memo">
</memo>
</ul>
<div>
<a class="waves-effect waves-light btn-large"
@click="fetchMemos({count: 10, type: currentTimeLine})">fetch more memos</a>
</div>
<div class="fixed-action-btn">
<router-link :to="{ name: 'newEditor' }"
class="btn-floating btn-large waves-effect waves-light red">
<i class="material-icons">add</i>
</router-link>
</div>
</div>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/Index.vue
<template>
<div class="index container">
<div class="filter">
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'public' }"
@click="switchTimeLine('public')">Public</a>
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'myself' }"
@click="switchTimeLine('myself')">Myself</a>
</div>
<ul class="collection">
<memo v-for="(memo, key) in orderedMemos" :memo="memo">
</memo>
</ul>
<div>
<a class="waves-effect waves-light btn-large"
@click="fetchMemos({count: 10, type: currentTimeLine})">fetch more memos</a>
</div>
<div class="fixed-action-btn">
<router-link :to="{ name: 'newEditor' }"
class="btn-floating btn-large waves-effect waves-light red">
<i class="material-icons">add</i>
</router-link>
</div>
</div>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/Index.vue
<template>
<div class="index container">
<div class="filter">
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'public' }"
@click="switchTimeLine('public')">Public</a>
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'myself' }"
@click="switchTimeLine('myself')">Myself</a>
</div>
<ul class="collection">
<memo v-for="(memo, key) in orderedMemos" :memo="memo">
</memo>
</ul>
<div>
<a class="waves-effect waves-light btn-large"
@click="fetchMemos({count: 10, type: currentTimeLine})">fetch more memos</a>
</div>
<div class="fixed-action-btn">
<router-link :to="{ name: 'newEditor' }"
class="btn-floating btn-large waves-effect waves-light red">
<i class="material-icons">add</i>
</router-link>
</div>
</div>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/Index.vue
<template>
<div class="index container">
<div class="filter">
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'public' }"
@click="switchTimeLine('public')">Public</a>
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'myself' }"
@click="switchTimeLine('myself')">Myself</a>
</div>
<ul class="collection">
<memo v-for="(memo, key) in orderedMemos" :memo="memo">
</memo>
</ul>
<div>
<a class="waves-effect waves-light btn-large"
@click="fetchMemos({count: 10, type: currentTimeLine})">fetch more memos</a>
</div>
<div class="fixed-action-btn">
<router-link :to="{ name: 'newEditor' }"
class="btn-floating btn-large waves-effect waves-light red">
<i class="material-icons">add</i>
</router-link>
</div>
</div>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/Index.vue
<template>
<div class="index container">
<div class="filter">
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'public' }"
@click="switchTimeLine('public')">Public</a>
<a class="waves-effect waves-light btn"
:class="{ active: currentTimeLine === 'myself' }"
@click="switchTimeLine('myself')">Myself</a>
</div>
<ul class="collection">
<memo v-for="(memo, key) in orderedMemos" :memo="memo">
</memo>
</ul>
<div>
<a class="waves-effect waves-light btn-large"
@click="fetchMemos({count: 10, type: currentTimeLine})">fetch more memos</a>
</div>
<div class="fixed-action-btn">
<router-link :to="{ name: 'newEditor' }"
class="btn-floating btn-large waves-effect waves-light red">
<i class="material-icons">add</i>
</router-link>
</div>
</div>
</template>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/Index.vue
<script>
import { mapGetters, mapActions } from 'vuex'
import Memo from './Memo.vue'
export default {
components: { Memo },
computed: {
...mapGetters(['memos', 'currentUserId']),
orderedMemos () {
// Here comes the implementation
}
},
data () {
return {
currentTimeLine: 'public'
}
},
methods: {
...mapActions(['fetchMemos']),
switchTimeLine (val) {
// Here comes the implementation
}
}
}
</script>
<style lang="stylus">
// Here comes some styling stuff
</style>
// https://github.com/akifo/vue-memo/blob/dev/src/js/components/Memo.vue
<template>
<li class="memo collection-item">
<router-link :to="{ name: 'viewer', params: { id: memo.key }}">
<h2>{{memo.title}}</h2>
<p>{{memo.author.name}} | {{memo.created | formatDate }}</p>
</router-link>
</li>
</template>
<script>
export default {
name: 'Memo',
props: ['memo', 'id']
}
</script>
<style lang="stylus">
// Here comes some styling stuff
</style>
You can pass arguments to this component via props
Development
× Setup vue
× Store
× Router
× Root app component
× Create components
× Dev tools
Dev tools
× vue-devtools
https://github.com/vuejs/vue-devtools
An introduction to Vue.js
An introduction to Vue.js
An introduction to Vue.js
Rails
Implicit dependencies
layer > concern hierarchy
You think about how to
store data
Mindset differences
Vue
Explicit dependencies
concern > layer hierarchy
You think about how to
present data
Authentication & authorization
Backend persistence
Async actions management (sagas)
Connecting to third party services
Styling
SSR?
Build process
Before going live you should know
after going live you should dive deeper in
Vuex
VueRouter
ES2015
Transitions & animations
GraphQL :-)
Functional programming
Progressive web apps
Vue native with Weex
Vue for desktop with Electron
SSR with Nuxt.js
(some of my) next learning paths
Web components & HTTP 2
Functional programming
TypeScript
Docker + GraphQL + Vue!!!
4.
what we learned
Building an application with Vue,
rails and graphql
Learning fact #1
If you need a “modern” UI
you should use something
like VUE.js
Learning fact #2
Developing with vue.js makes
working with JS more easy
to reason about
Learning fact #3
Working with vue.js implies
bigger effort…
...just because you need
something bigger
THANKS!Any questions?
You can find ME at @eLafo
You can find US at @aspgems & info@aspgems.com
References
× Learning Vue.js 2 - Olga Filipova
× https://vuejs.org/
× http://engineering.paiza.io/entry/2015/03/12/145216
× https://medium.com/js-dojo/whats-new-in-vue-js-2-0-virtual-dom-dc4b5b827f40
× http://www.valuecoders.com/blog/technology-and-apps/vue-js-comparison-angular-re
act/
× https://medium.com/@deathmood/how-to-write-your-own-virtual-dom-ee74acc13060
×
Credits
Special thanks to all the people who made and
released these awesome resources for free:
× Vue example app by afiko
× Presentation template by SlidesCarnival
× Photographs by Startupstockphotos
Presentation design
This presentation uses the following typographies:
× Titles: Bangers
× Body copy: Sniglet
You can download the fonts on this page:
https://www.google.com/fonts#UsePlace:use/Collection:Sniglet|Bangers
Click on the “arrow button” that appears on the top right
You don’t need to keep this slide in your presentation. It’s only here to serve you as a design guide if
you need to create new slides or download the fonts to edit the presentation in PowerPoint®

More Related Content

What's hot (20)

PDF
Vue.js for beginners
Julio Bitencourt
 
PDF
Vue, vue router, vuex
Samundra khatri
 
ODP
An Introduction to Vuejs
Paddy Lock
 
PPTX
An introduction to Vue.js
Pagepro
 
PPTX
Introduction to node.js
Dinesh U
 
PPTX
Introduction to spring boot
Santosh Kumar Kar
 
PPTX
Reactjs
Mallikarjuna G D
 
PDF
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Edureka!
 
PPTX
React workshop
Imran Sayed
 
PDF
An introduction to React.js
Emanuele DelBono
 
PPTX
Intro to React
Justin Reock
 
PPTX
React JS - A quick introduction tutorial
Mohammed Fazuluddin
 
ODP
Introduction to Swagger
Knoldus Inc.
 
PDF
NodeJS for Beginner
Apaichon Punopas
 
PDF
Angular - Chapter 7 - HTTP Services
WebStackAcademy
 
PPTX
React + Redux Introduction
Nikolaus Graf
 
PDF
Angular - Chapter 4 - Data and Event Handling
WebStackAcademy
 
PPTX
Introduction to Node js
Akshay Mathur
 
PPTX
Introduction to React
Rob Quick
 
Vue.js for beginners
Julio Bitencourt
 
Vue, vue router, vuex
Samundra khatri
 
An Introduction to Vuejs
Paddy Lock
 
An introduction to Vue.js
Pagepro
 
Introduction to node.js
Dinesh U
 
Introduction to spring boot
Santosh Kumar Kar
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Edureka!
 
React workshop
Imran Sayed
 
An introduction to React.js
Emanuele DelBono
 
Intro to React
Justin Reock
 
React JS - A quick introduction tutorial
Mohammed Fazuluddin
 
Introduction to Swagger
Knoldus Inc.
 
NodeJS for Beginner
Apaichon Punopas
 
Angular - Chapter 7 - HTTP Services
WebStackAcademy
 
React + Redux Introduction
Nikolaus Graf
 
Angular - Chapter 4 - Data and Event Handling
WebStackAcademy
 
Introduction to Node js
Akshay Mathur
 
Introduction to React
Rob Quick
 

Similar to An introduction to Vue.js (20)

PDF
Love at first Vue
Dalibor Gogic
 
PPTX
Nodejs.meetup
Vivian S. Zhang
 
PPTX
Server Side Rendering with Nuxt.js
Jessie Barnett
 
PDF
20130528 solution linux_frousseau_nopain_webdev
Frank Rousseau
 
PPTX
Let's react - Meetup
RAJNISH KATHAROTIYA
 
PDF
Introducing Rendr: Run your Backbone.js apps on the client and server
Spike Brehm
 
PDF
Building and deploying React applications
Astrails
 
PPTX
How to Build SPA with Vue Router 2.0
Takuya Tejima
 
PPTX
Reactive application using meteor
Sapna Upreti
 
PDF
How to Webpack your Django!
David Gibbons
 
PDF
Adding custom ui controls to your application (1)
Oro Inc.
 
PDF
Rest web service_with_spring_hateoas
Zeid Hassan
 
PDF
Moving from AS3 to Flex - advantages, hazards, traps
Florian Weil
 
PDF
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
John Hann
 
PDF
Deploying configurable frontend web application containers
José Moreira
 
PPTX
Writing a massive javascript app
Justin Park
 
PDF
Introduction to VueJS & Vuex
Bernd Alter
 
PDF
More Secrets of JavaScript Libraries
jeresig
 
PDF
Migrating from Struts 1 to Struts 2
Matt Raible
 
PDF
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
OdessaJS Conf
 
Love at first Vue
Dalibor Gogic
 
Nodejs.meetup
Vivian S. Zhang
 
Server Side Rendering with Nuxt.js
Jessie Barnett
 
20130528 solution linux_frousseau_nopain_webdev
Frank Rousseau
 
Let's react - Meetup
RAJNISH KATHAROTIYA
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Spike Brehm
 
Building and deploying React applications
Astrails
 
How to Build SPA with Vue Router 2.0
Takuya Tejima
 
Reactive application using meteor
Sapna Upreti
 
How to Webpack your Django!
David Gibbons
 
Adding custom ui controls to your application (1)
Oro Inc.
 
Rest web service_with_spring_hateoas
Zeid Hassan
 
Moving from AS3 to Flex - advantages, hazards, traps
Florian Weil
 
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
John Hann
 
Deploying configurable frontend web application containers
José Moreira
 
Writing a massive javascript app
Justin Park
 
Introduction to VueJS & Vuex
Bernd Alter
 
More Secrets of JavaScript Libraries
jeresig
 
Migrating from Struts 1 to Struts 2
Matt Raible
 
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
OdessaJS Conf
 
Ad

More from Javier Lafora Rey (8)

PPTX
Modular development with redux
Javier Lafora Rey
 
ODP
Understanding big data-drupalcamp
Javier Lafora Rey
 
ODP
API REST for beginners or why you should make your API understandable
Javier Lafora Rey
 
PDF
APIs para gente normal
Javier Lafora Rey
 
ODP
¿Por qué ruby on rails?
Javier Lafora Rey
 
ODP
Ruby object model: A matter of life and death
Javier Lafora Rey
 
ODP
ROA - Resource Oriented Architecture
Javier Lafora Rey
 
ODP
How to use git without rage
Javier Lafora Rey
 
Modular development with redux
Javier Lafora Rey
 
Understanding big data-drupalcamp
Javier Lafora Rey
 
API REST for beginners or why you should make your API understandable
Javier Lafora Rey
 
APIs para gente normal
Javier Lafora Rey
 
¿Por qué ruby on rails?
Javier Lafora Rey
 
Ruby object model: A matter of life and death
Javier Lafora Rey
 
ROA - Resource Oriented Architecture
Javier Lafora Rey
 
How to use git without rage
Javier Lafora Rey
 
Ad

Recently uploaded (20)

PPTX
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
PPTX
The Role of a PHP Development Company in Modern Web Development
SEO Company for School in Delhi NCR
 
PDF
Executive Business Intelligence Dashboards
vandeslie24
 
PPTX
Feb 2021 Cohesity first pitch presentation.pptx
enginsayin1
 
PDF
Streamline Contractor Lifecycle- TECH EHS Solution
TECH EHS Solution
 
PPTX
Human Resources Information System (HRIS)
Amity University, Patna
 
PDF
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
PPTX
Tally_Basic_Operations_Presentation.pptx
AditiBansal54083
 
PDF
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
PPTX
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
PPTX
Tally software_Introduction_Presentation
AditiBansal54083
 
PDF
Capcut Pro Crack For PC Latest Version {Fully Unlocked} 2025
hashhshs786
 
PDF
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
PDF
Revenue streams of the Wazirx clone script.pdf
aaronjeffray
 
PDF
Understanding the Need for Systemic Change in Open Source Through Intersectio...
Imma Valls Bernaus
 
PDF
Salesforce CRM Services.VALiNTRY360
VALiNTRY360
 
PPTX
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
PDF
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
PPTX
Java Native Memory Leaks: The Hidden Villain Behind JVM Performance Issues
Tier1 app
 
PDF
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
The Role of a PHP Development Company in Modern Web Development
SEO Company for School in Delhi NCR
 
Executive Business Intelligence Dashboards
vandeslie24
 
Feb 2021 Cohesity first pitch presentation.pptx
enginsayin1
 
Streamline Contractor Lifecycle- TECH EHS Solution
TECH EHS Solution
 
Human Resources Information System (HRIS)
Amity University, Patna
 
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
Tally_Basic_Operations_Presentation.pptx
AditiBansal54083
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
Tally software_Introduction_Presentation
AditiBansal54083
 
Capcut Pro Crack For PC Latest Version {Fully Unlocked} 2025
hashhshs786
 
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
Revenue streams of the Wazirx clone script.pdf
aaronjeffray
 
Understanding the Need for Systemic Change in Open Source Through Intersectio...
Imma Valls Bernaus
 
Salesforce CRM Services.VALiNTRY360
VALiNTRY360
 
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
Java Native Memory Leaks: The Hidden Villain Behind JVM Performance Issues
Tier1 app
 
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 

An introduction to Vue.js