SlideShare una empresa de Scribd logo
1 de 149
Descargar para leer sin conexión
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.
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
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
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
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®

Más contenido relacionado

La actualidad más candente

An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.jsPagepro
 
Vue.js Getting Started
Vue.js Getting StartedVue.js Getting Started
Vue.js Getting StartedMurat Doğan
 
The Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.jsThe Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.jsHolly Schinsky
 
introduction to Vue.js 3
introduction to Vue.js 3 introduction to Vue.js 3
introduction to Vue.js 3 ArezooKmn
 
State manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to VuexState manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to VuexCommit University
 
An introduction to React.js
An introduction to React.jsAn introduction to React.js
An introduction to React.jsEmanuele DelBono
 
VueJS Best Practices
VueJS Best PracticesVueJS Best Practices
VueJS Best PracticesFatih Acet
 
Spring boot introduction
Spring boot introductionSpring boot introduction
Spring boot introductionRasheed Waraich
 
Deep dive into Vue.js
Deep dive into Vue.jsDeep dive into Vue.js
Deep dive into Vue.js선협 이
 
Basics of Vue.js 2019
Basics of Vue.js 2019Basics of Vue.js 2019
Basics of Vue.js 2019Paul Bele
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring bootAntoine Rey
 
Node.js Express
Node.js  ExpressNode.js  Express
Node.js ExpressEyal Vardi
 
Introduction to VueJS & Vuex
Introduction to VueJS & VuexIntroduction to VueJS & Vuex
Introduction to VueJS & VuexBernd Alter
 

La actualidad más candente (20)

An introduction to Vue.js
An introduction to Vue.jsAn introduction to Vue.js
An introduction to Vue.js
 
Vue.js Getting Started
Vue.js Getting StartedVue.js Getting Started
Vue.js Getting Started
 
Vue, vue router, vuex
Vue, vue router, vuexVue, vue router, vuex
Vue, vue router, vuex
 
The Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.jsThe Point of Vue - Intro to Vue.js
The Point of Vue - Intro to Vue.js
 
Vue JS Intro
Vue JS IntroVue JS Intro
Vue JS Intro
 
introduction to Vue.js 3
introduction to Vue.js 3 introduction to Vue.js 3
introduction to Vue.js 3
 
State manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to VuexState manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to Vuex
 
Introduction à React
Introduction à ReactIntroduction à React
Introduction à React
 
Introduction to thymeleaf
Introduction to thymeleafIntroduction to thymeleaf
Introduction to thymeleaf
 
An introduction to React.js
An introduction to React.jsAn introduction to React.js
An introduction to React.js
 
VueJS Best Practices
VueJS Best PracticesVueJS Best Practices
VueJS Best Practices
 
Spring boot introduction
Spring boot introductionSpring boot introduction
Spring boot introduction
 
Deep dive into Vue.js
Deep dive into Vue.jsDeep dive into Vue.js
Deep dive into Vue.js
 
Basics of Vue.js 2019
Basics of Vue.js 2019Basics of Vue.js 2019
Basics of Vue.js 2019
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring boot
 
Vue 2 vs Vue 3.pptx
Vue 2 vs Vue 3.pptxVue 2 vs Vue 3.pptx
Vue 2 vs Vue 3.pptx
 
Angular Observables & RxJS Introduction
Angular Observables & RxJS IntroductionAngular Observables & RxJS Introduction
Angular Observables & RxJS Introduction
 
Node.js Express
Node.js  ExpressNode.js  Express
Node.js Express
 
Introduction to VueJS & Vuex
Introduction to VueJS & VuexIntroduction to VueJS & Vuex
Introduction to VueJS & Vuex
 
React JS
React JSReact JS
React JS
 

Similar a An introduction to Vue.js

Server Side Rendering with Nuxt.js
Server Side Rendering with Nuxt.jsServer Side Rendering with Nuxt.js
Server Side Rendering with Nuxt.jsJessie Barnett
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdevFrank Rousseau
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverSpike Brehm
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applicationsAstrails
 
How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0Takuya Tejima
 
Reactive application using meteor
Reactive application using meteorReactive application using meteor
Reactive application using meteorSapna Upreti
 
How to Webpack your Django!
How to Webpack your Django!How to Webpack your Django!
How to Webpack your Django!David Gibbons
 
Adding custom ui controls to your application (1)
Adding custom ui controls to your application (1)Adding custom ui controls to your application (1)
Adding custom ui controls to your application (1)Oro Inc.
 
Rest web service_with_spring_hateoas
Rest web service_with_spring_hateoasRest web service_with_spring_hateoas
Rest web service_with_spring_hateoasZeid Hassan
 
Moving from AS3 to Flex - advantages, hazards, traps
Moving from AS3 to Flex - advantages, hazards, trapsMoving from AS3 to Flex - advantages, hazards, traps
Moving from AS3 to Flex - advantages, hazards, trapsFlorian Weil
 
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014John Hann
 
Deploying configurable frontend web application containers
Deploying configurable frontend web application containersDeploying configurable frontend web application containers
Deploying configurable frontend web application containersJosé Moreira
 
Writing a massive javascript app
Writing a massive javascript appWriting a massive javascript app
Writing a massive javascript appJustin Park
 
More Secrets of JavaScript Libraries
More Secrets of JavaScript LibrariesMore Secrets of JavaScript Libraries
More Secrets of JavaScript Librariesjeresig
 
Migrating from Struts 1 to Struts 2
Migrating from Struts 1 to Struts 2Migrating from Struts 1 to Struts 2
Migrating from Struts 1 to Struts 2Matt Raible
 
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...OdessaJS Conf
 
How to create your own Volto site!
How to create your own Volto site!How to create your own Volto site!
How to create your own Volto site!Rob Gietema
 

Similar a An introduction to Vue.js (20)

Love at first Vue
Love at first VueLove at first Vue
Love at first Vue
 
Nodejs.meetup
Nodejs.meetupNodejs.meetup
Nodejs.meetup
 
Server Side Rendering with Nuxt.js
Server Side Rendering with Nuxt.jsServer Side Rendering with Nuxt.js
Server Side Rendering with Nuxt.js
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev
 
Let's react - Meetup
Let's react - MeetupLet's react - Meetup
Let's react - Meetup
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applications
 
How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0How to Build SPA with Vue Router 2.0
How to Build SPA with Vue Router 2.0
 
Reactive application using meteor
Reactive application using meteorReactive application using meteor
Reactive application using meteor
 
How to Webpack your Django!
How to Webpack your Django!How to Webpack your Django!
How to Webpack your Django!
 
Adding custom ui controls to your application (1)
Adding custom ui controls to your application (1)Adding custom ui controls to your application (1)
Adding custom ui controls to your application (1)
 
Rest web service_with_spring_hateoas
Rest web service_with_spring_hateoasRest web service_with_spring_hateoas
Rest web service_with_spring_hateoas
 
Moving from AS3 to Flex - advantages, hazards, traps
Moving from AS3 to Flex - advantages, hazards, trapsMoving from AS3 to Flex - advantages, hazards, traps
Moving from AS3 to Flex - advantages, hazards, traps
 
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
 
Deploying configurable frontend web application containers
Deploying configurable frontend web application containersDeploying configurable frontend web application containers
Deploying configurable frontend web application containers
 
Writing a massive javascript app
Writing a massive javascript appWriting a massive javascript app
Writing a massive javascript app
 
More Secrets of JavaScript Libraries
More Secrets of JavaScript LibrariesMore Secrets of JavaScript Libraries
More Secrets of JavaScript Libraries
 
Migrating from Struts 1 to Struts 2
Migrating from Struts 1 to Struts 2Migrating from Struts 1 to Struts 2
Migrating from Struts 1 to Struts 2
 
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
 
How to create your own Volto site!
How to create your own Volto site!How to create your own Volto site!
How to create your own Volto site!
 

Más de Javier Lafora Rey

Modular development with redux
Modular development with reduxModular development with redux
Modular development with reduxJavier Lafora Rey
 
Understanding big data-drupalcamp
Understanding big data-drupalcampUnderstanding big data-drupalcamp
Understanding big data-drupalcampJavier Lafora Rey
 
API REST for beginners or why you should make your API understandable
API REST for beginners or why you should make your API understandableAPI REST for beginners or why you should make your API understandable
API REST for beginners or why you should make your API understandableJavier Lafora Rey
 
Ruby object model: A matter of life and death
Ruby object model: A matter of life and deathRuby object model: A matter of life and death
Ruby object model: A matter of life and deathJavier Lafora Rey
 
ROA - Resource Oriented Architecture
ROA - Resource Oriented ArchitectureROA - Resource Oriented Architecture
ROA - Resource Oriented ArchitectureJavier Lafora Rey
 

Más de Javier Lafora Rey (8)

Modular development with redux
Modular development with reduxModular development with redux
Modular development with redux
 
Understanding big data-drupalcamp
Understanding big data-drupalcampUnderstanding big data-drupalcamp
Understanding big data-drupalcamp
 
API REST for beginners or why you should make your API understandable
API REST for beginners or why you should make your API understandableAPI REST for beginners or why you should make your API understandable
API REST for beginners or why you should make your API understandable
 
APIs para gente normal
APIs para gente normalAPIs para gente normal
APIs para gente normal
 
¿Por qué ruby on rails?
¿Por qué ruby on rails?¿Por qué ruby on rails?
¿Por qué ruby on rails?
 
Ruby object model: A matter of life and death
Ruby object model: A matter of life and deathRuby object model: A matter of life and death
Ruby object model: A matter of life and death
 
ROA - Resource Oriented Architecture
ROA - Resource Oriented ArchitectureROA - Resource Oriented Architecture
ROA - Resource Oriented Architecture
 
How to use git without rage
How to use git without rageHow to use git without rage
How to use git without rage
 

Último

Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 
Software Coding for software engineering
Software Coding for software engineeringSoftware Coding for software engineering
Software Coding for software engineeringssuserb3a23b
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfkalichargn70th171
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 

Último (20)

Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 
Software Coding for software engineering
Software Coding for software engineeringSoftware Coding for software engineering
Software Coding for software engineering
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 

An introduction to Vue.js

  • 2. Hello!Javier Lafora CTO at ASPgems @eLafo
  • 3. At the moment of preparing this talk the latest version of vue is 2.3
  • 4. 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
  • 5. 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
  • 6. 1. Why? 3 reasons why you would want to use a proper front-end framework
  • 7. 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.
  • 8.
  • 9.
  • 10. 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.
  • 11. 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
  • 13. Declarative Tell what to render instead of how
  • 14.
  • 15. MVVM
  • 18. 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.
  • 20. Components concerns × Structure -> <Template> × Presentation -> <Style>
  • 21. Components concerns × Structure -> <Template> × Presentation -> <Styles> × Data -> <Script>
  • 24. 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
  • 25. 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
  • 29. Component internals × data: Internal state × props: args
  • 30. Component internals × data: Internal state × props: args × computed: Cacheable read functions
  • 31. Component internals × data: Internal state × props: args × computed: Cacheable read functions × methods: Event handling
  • 32. Component internals × data: Internal state × props: args × computed: Cacheable read functions × methods: Event handling × Provided by plugins: Extensions
  • 33. Vuex
  • 34. Vuex × state: application state as a tree
  • 35. Vuex × state: application state as a tree × getters: state read methods
  • 36. Vuex × state: application state as a tree × getters: state read methods × mutations: state write methods
  • 37. Vuex × state: application state as a tree × getters: state read methods × mutations: state write methods × actions: user commands
  • 38. Vuerouter + vuex-router-sync × state.route × path × params × query
  • 40.
  • 42. My mental process is top-down Component identification Model tree design Development
  • 43. Place your screenshot here Memo-vue Let’s use this example from https://github.com/akifo/vue-memo
  • 47. Place your screenshot here Identifying components × AppHeader × [Main content] × Index
  • 48. Place your screenshot here Identifying components × AppHeader × [Main content] × Index × Memo
  • 49. Place your screenshot here Identifying components × AppHeader × [Main content] × Index × Memo × Viewer
  • 50. Place your screenshot here Identifying components × AppHeader × [Main content] × Index × Memo × Viewer × Editor
  • 52. State { // route: {} // vue-router has created state.route }
  • 53. State { user: {}, memos: {} // route: {} // vue-router has created state.route }
  • 54. State { user: { loggedIn: false }, memos: {} // route: {} // vue-router has created state.route }
  • 55. State { user: { loggedIn: false, uid: '' }, memos: {} // route: {} // vue-router has created state.route }
  • 56. State { user: { loggedIn: false, uid: '', name: '' }, memos: {} // route: {} // vue-router has created state.route }
  • 57. State { user: { loggedIn: false, uid: '', name: '', profilePicUrl: '' }, memos: {} // route: {} // vue-router has created state.route }
  • 58. State { user: { loggedIn: false, uid: '', name: '', profilePicUrl: '' }, memos: {} // route: {} // vue-router has created state.route } memo = { title: '', body: '' }
  • 64. getters Memos × memos × currentMemo × currentMemoId User × user
  • 65. getters Memos × memos × currentMemo × currentMemoId User × user × currentUserName
  • 66. getters Memos × memos × currentMemo × currentMemoId User × user × currentUserName × currentUserId
  • 72. Mutations Memos × setMemo × setMemos × deleteMemo User × oAuthStateChanged
  • 73. Mutations Memos × setMemo × setMemos × deleteMemo User × oAuthStateChanged × setUser
  • 74. Mutations Memos × setMemo × setMemos × deleteMemo User × oAuthStateChanged × setUser
  • 75. Modeling × State × Getters × mutations × Actions
  • 79. Actions User × onAuthStateChanged × signIn × signOut × setUserInfo Memos
  • 80. Actions User × onAuthStateChanged × signIn × signOut × setUserInfo Memos × fetchMemos
  • 81. Actions User × onAuthStateChanged × signIn × signOut × setUserInfo Memos × fetchMemos × fetchMemo
  • 82. Actions User × onAuthStateChanged × signIn × signOut × setUserInfo Memos × fetchMemos × fetchMemo × addMemo
  • 83. Actions User × onAuthStateChanged × signIn × signOut × setUserInfo Memos × fetchMemos × fetchMemo × addMemo × deleteMemo
  • 84. Actions User × onAuthStateChanged × signIn × signOut × setUserInfo Memos × fetchMemos × fetchMemo × addMemo × deleteMemo × updateMemo
  • 87. // 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
  • 88. // 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
  • 89. // 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
  • 90. // 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
  • 92. // 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 })
  • 93. // 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
  • 94. // 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!!!
  • 95. // 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
  • 96. 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) } }
  • 97. 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
  • 98. 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
  • 99. 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 }
  • 100. Development × Setup vue × Store × Router
  • 101. // 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
  • 102. // 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
  • 103. // 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!!!
  • 104. // 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
  • 105. Development × Setup vue × Store × Router × Root app component
  • 106. // 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>
  • 107. // 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
  • 108. // 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
  • 109. // 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
  • 110. // 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
  • 111. Development × Setup vue × Store × Router × Root app component × Create components
  • 112. // 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>
  • 113. // 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>
  • 114. // 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>
  • 115. // 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>
  • 116. // 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>
  • 117. // 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>
  • 118. // 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>
  • 119. // 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>
  • 120. // 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>
  • 121. // 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>
  • 122. // 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>
  • 123. // 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>
  • 124. 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() }) } } }
  • 125. 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() }) }
  • 126. // 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>
  • 127. // 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>
  • 128. // 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>
  • 129. // 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>
  • 130. // 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>
  • 131. // 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>
  • 132. // 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
  • 133. Development × Setup vue × Store × Router × Root app component × Create components × Dev tools
  • 135.
  • 136.
  • 137.
  • 138. 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
  • 139. Authentication & authorization Backend persistence Async actions management (sagas) Connecting to third party services Styling SSR? Build process Before going live you should know
  • 140. after going live you should dive deeper in Vuex VueRouter ES2015 Transitions & animations GraphQL :-) Functional programming
  • 141. 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!!!
  • 142. 4. what we learned Building an application with Vue, rails and graphql
  • 143. Learning fact #1 If you need a “modern” UI you should use something like VUE.js
  • 144. Learning fact #2 Developing with vue.js makes working with JS more easy to reason about
  • 145. Learning fact #3 Working with vue.js implies bigger effort… ...just because you need something bigger
  • 146. THANKS!Any questions? You can find ME at @eLafo You can find US at @aspgems & info@aspgems.com
  • 147. 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 ×
  • 148. 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
  • 149. 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®