KEMBAR78
Modern frontend development with VueJs | PDF
Modern frontend development
with Vue JS
Tudor Barbu
@motanelu
www.linkedin.com/in/tudorbarbu
https://github.com/motanelu
How old are
you? :)
Tools provided by early JS frameworks:
● AJAX abstractions
● Simple DOM manipulation
● Event management
● Annoying animations
● Shorthand methods*
Paleolithic 2.0
* not really a good thing
Missing pieces
● DOM abstractions / templating
● URL management / routing
● State management
● Support for reusable components
● Coding standard and guidelines
$(document).ready(function () {
$('#refresh-button').click(function () {
$('#news-container').load('/latest-news')
})
})
$(function () {
$('#refresh-button').on('click', function () {
$.ajax('/latest-news', {
success: function (data, textStatus, jqXHR) {
var html = createHTMLFromJsonResponse(data)
$('#news-container').html(html)
}
})
})
})
Inconsistent event handling
Inefficient DOM operations
<div class="article" id="article-5">
<h2>The awesome title</h2>
<p>
Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Donec hendrerit nunc
turpis, quis maximus egestas sit amet.
</p>
<span class="likes">2<span>
</div>
/* content at t1 (initial state) */
article = {
id: '5',
title: 'The awesome title',
content: 'Lorem ipsum dolor sit amet...',
likes: 2
}
/* content at t2 (after update) */
article = {
id: '5',
title: 'The awesome title',
content: 'Lorem ipsum dolor sit amet...',
likes: 4
}
Virtual DOM
diff
patch
<ul class="product-menu">
<li data-item-id="65017da4">Product one</li>
<li data-item-id="6501816e">Product two</li>
</ul>
$('.product-menu').on('click', 'li', function () {
var id = $(this).data('item-id')
loadProduct(id)
})
Keeping state in the DOM
(slow)
<ul class="menu">
<li data-href="index">Home</li>
<li data-href="contact">Contact</li>
</ul>
<div id="page-content">
<!-- content loaded via AJAX -->
<form id="content-form">
<!-- ... fields ...-->
<button type="button" class="submit">
Contact us
</button>
</form>
</div>
$('#content-form .submit').click(function () {
var form = $('#content-form')
var isValid = validate(form)
if (isValid) {
postTheForm(form)
}
})
Where do I fit in?
Every Javascript project
after 6 to 9 months
When your framework is just right:
● Separation of concerns
● Virtual DOM
● In memory state management
● Routing (HTML5 History API)
● Clear coding practices
● AJAX (nice to have)
Why Vue.js?
● Lightweight
● “True” open-source
● Fastest learning curve
● Separation of concerns in one file
● Great performance per KB *
* just invented this metric
DOM operations performance
Source: http://www.stefankrause.net/js-frameworks-benchmark6/webdriver-ts-results/table.html
Sources:
1. http://www.stefankrause.net/js-frameworks-benchmark6/webdriver-ts-results/table.html
2. https://gist.github.com/Restuta/cda69e50a853aa64912d
RAM usage & filesize
Components
Content.vue
Footer.vue
MenuItem.vue
selected
Components are
● Custom HTML elements with behaviour attached to
them
● Self-contained
● Reside in .vue files (compiled with Webpack)
● Can be mixed with regular HTML
● The Vue application is wrapped in a root
component, usually called App.vue
<template>
<div class="content">
<div v-if="loaded">
<h1>{{ title }}</h1>
<img :src="media" :alt="title">
<p>{{ article }}</p>
</div>
<div v-else>Loading...</div>
</div>
</template>
<script>
export default {
name: 'content',
data () {
return {
loaded: false,
title: null,
article: null,
media: null
}
},
mounted () {
axios.get('/latest-news').then(response => {
this.title = response.data.title,
this.article = response.data.article
this.media = response.data.media
this.loaded = true
})
}
}
</script>
<style>
h1 {
font-family: "Comic Sans" /* on purpose :) */
}
</style>
“Reactive” properties
Lifecycle events
HTML template
presentation logic
CSS styling
Javascript part
component logic
Content.vue
<!-- ... -->
<div v-if="loaded">
<h1>{{ title }}</h1>
<img :src="media" :alt="title">
</div>
<!-- ... -->
/* ... */
data () {
return {
loaded: false,
title: null,
media: null
}
}
/* ... */
show / hide elements
fill in HTML elements
values for HTML attributes
Reactive properties
Changing a reactive property triggers a re-render of the associated DOM
<template>
<div class="content">
<div v-if="loaded">
<h1>{{ title }}</h1>
<p>{{ article }}</p>
</div>
<div v-else>
Loading...
</div>
</div>
</template>
<script>
export default {
name: 'content',
data () {
return {
title: null,
article: null
}
},
computed: {
loaded () {
return title !== null && article !== null
}
},
mounted () {
// ...
}
}
</script>
<style>
h1 {
font-family: "Comic Sans" /* on purpose :) */
}
</style>
Computed properties
Computed properties:
● Functions that wrap complex logic
● Cached based on their dependencies
<template>
<!-- display article -->
<a href="#" v-on:click="toggle()">{{ toggleMessage }}</a>
<div v-if="showRelated">
<ul>
<li v-for="article in related">
<a href="{{ article.url }}"> {{ article.text }}</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'content',
data () {
return {
/* ... */
showRelated: false,
related: [
{ url: '/path/to/first-article', text: 'First related article' },
{ url: '/path/to/second-article', text: 'Second related article' }
]
}
},
computed: {
toggleMessage () {
return !this.showRelated ? 'Show related articles' : 'Hide related articles'
}
},
methods: {
toggle() {
this.showRelated = !this.showRelated
}
}
}
</script>
Iterate through properties
HTML-like syntax for
event handlers
Computed properties can
be anything
HTML event handlers!?!
● Easier to locate handlers just by skimming the template
● Easier to test the underlying logic since it’s completely separated
from the DOM
● They can be automatically removed together with their associated
HTML code
<template>
<header>
<ul>
<menu-item id="1" label="books" />
<menu-item id="2" label="pens" />
</ul>
<header>
<div class="main">
<content>
</div>
<footer>
</template>
<script>
import MenuItem from '/path/to/MenuItem.vue'
import Content from '/path/to/Content.vue'
import Footer from '/path/to/Footer.vue'
export default {
name: 'app',
components: {
'menu-item': MenuItem,
'content': Content
'footer': Footer
}
/* ... */
}
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
template: '<App />',
components: { App }
})
App.vue main.js
Components
usage
Parent
Child
publishevents
injectproperties
2 Way data communication
● Parents inject values via HTML attributes
● Children push custom events using the
v-on:{custom-event} format
<template>
<ul>
<menu-item
id="1"
label="books"
v-on:selected="process($event)" />
<menu-item
id="2"
label="pens"
selected="true""
v-on:selected="process($event) />
</ul>
</template>
<script>
import MenuItem from './path/to/MenuItem.vue'
export default {
name: 'top-menu',
components: {
'menu-item': MenuItem
},
methods: {
process ($event) {
// select the category
}
}
}
</script>
<template>
<li :class="{'active-element': selected}">
<a v-on:click="select()">
{{ label }}
</a>
</li>
</template>
<script>
export default {
name: 'MenuItem',
props: ['id', 'label', 'selected'],
methods: {
select () {
this.$emit('selected', { id: this.id })
}
}
}
</script>
Properties
Events
App.vue MenuItem.vue
/* Content.vue */
export default {
/* ... */
data () {
return {
title: null,
article: null
}
},
computed: {
loaded () {
return article !== null && title !== null
}
},
props: [
'articleId'
],
watch: {
articleId: (value, previous) {
axios.get(`/article/{$value}`).then(response => {
this.title = response.data.title,
this.article = response.data.article
})
}
}
}
<!-- App.vue -->
<template>
<ul class="menu">
<li v-on:click="loadArticle(1)">
First article
</li>
<li v-on:click="loadArticle(2)">
Second article
</li>
<!-- ... -->
</ul>
<content :articleId="articleId" />
</template>
<script>
/* ... */
export default {
data () {
return {
articleId: null
}
},
methods: {
loadArticle(articleId) {
this.articleId = articleId
}
}
}
</script>
Reactive properties
Changes on the articleId are propagated
to the child
Encapsulated functionality
Everything about loading and
processing the content is encapsulated
$ npm install vue-router --save
Not this kind of router :)
Vue Router
Vue Routing
● Provides stateful URLs
● Supports both HTML5 history API as well as the # as
fallback
import Vue from 'vue'
import Router from 'vue-router'
import Homepage from '/path/Home.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Homepage
}
]
})
import Vue from 'vue'
import router from '/path/to/router'
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App />',
components: { App }
})
<template>
<!-- App.vue -->
<router-view />
</template>
1 2 3
router.js main.js App.vue
Other features:
● dynamic routes (/article/:id)
● nested routes
● programmatic navigation
● allows adding watchers on routes
Image from https://vuex.vuejs.org/en/intro.html
$ npm install vuex --save
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const FETCH_START = 'FETCH_START'
const FETCH_SUCCESS = 'FETCH_SUCCESS'
const FETCH_ERROR = 'FETCH_ERROR'
export default new Vuex.Store ({
state: {
loading: false,
error: null,
article: null
},
mutations: {
[FETCH_START](state) {
state.loading = true
state.error = null
},
[FETCH_SUCCESS](state, article) {
state.error = null
state.loading = false
state.article = article
},
[FETCH_ERROR](state) {
state.loading = false
state.error = true
}
},
actions: {
load({ commit }, { articleId }) {
commit(FETCH_START)
axios.get(`/articles/${articleId}`)
.then(article => commit(FETCH_SUCCESS, article))
.catch(() => commit(FETCH_ERROR))
}
}
})
Constants for mutation types
(common pattern)
State variables
Mutations
(need to be synchronous)
Vuexmodule
Actions
(can be asynchronous & they end
with a mutation)
<template>
<ul class="menu">
<li v-on:click="loadArticle(1)">
First article
</li>
<li v-on:click="loadArticle(2)">
Second article
</li>
<!-- ... -->
</ul>
<content />
</template>
<script>
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions({
'loadArticle': 'load'
}),
// other component specific methods
}
}
</script>
UsingVuex
Map Vuex actions in the local scope
( mapAction helper & spread operator)
Templates stay the same
Vuex
Menu.vue
(update selected item)
Content.vue
(display the content)
Breadcrumbs.vue
(display the current node)
App.vue
(update the <title>)
import { mapState } from 'vuex'
export default {
computed: {
...mapState({
loading: state => state.loading,
error: state => state.error,
article: state => state.article
})
},
watch: {
article: (value, previous) {
// trigger an update
}
error: (value) {
if (value) {
// signal an error to the user
}
}
}
}
HTML event handlers!?!
● Efficient DOM manipulation with Virtual DOM
●
● lightweight
● efficient DOM manipulation
● separation of concerns & encapsulation
● routing (HTML5 History API & hash sign)
● state management with Vuex
$ npm install -g vue-cli
$ vue init webpack my-first-vue-project
Vue CLI
● Quickly start a new project
● Multiple templates and build systems *
● Sets up linters (AirBNB or Standard)
● Unit tests (karma)
● End to end tests (Nightwatch)
* if in doubt, use Webpack
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en
Features
● Inspect components in real
time
● Inspect Vuex status
● “Timetravel” between Vuex
commits
Resources
● vuejs.org
● router.vuejs.org
● vuex.vuejs.org
● vuejs/awesome-vue
Thank you
@Schibsted_Eng
bytes.schibsted.com
Tudor Barbu
@motanelu
www.linkedin.com/in/tudorbarbu
https://github.com/motanelu

Modern frontend development with VueJs

  • 1.
    Modern frontend development withVue JS Tudor Barbu @motanelu www.linkedin.com/in/tudorbarbu https://github.com/motanelu
  • 3.
  • 4.
    Tools provided byearly JS frameworks: ● AJAX abstractions ● Simple DOM manipulation ● Event management ● Annoying animations ● Shorthand methods* Paleolithic 2.0 * not really a good thing
  • 5.
    Missing pieces ● DOMabstractions / templating ● URL management / routing ● State management ● Support for reusable components ● Coding standard and guidelines
  • 6.
    $(document).ready(function () { $('#refresh-button').click(function() { $('#news-container').load('/latest-news') }) }) $(function () { $('#refresh-button').on('click', function () { $.ajax('/latest-news', { success: function (data, textStatus, jqXHR) { var html = createHTMLFromJsonResponse(data) $('#news-container').html(html) } }) }) }) Inconsistent event handling Inefficient DOM operations
  • 7.
    <div class="article" id="article-5"> <h2>Theawesome title</h2> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec hendrerit nunc turpis, quis maximus egestas sit amet. </p> <span class="likes">2<span> </div> /* content at t1 (initial state) */ article = { id: '5', title: 'The awesome title', content: 'Lorem ipsum dolor sit amet...', likes: 2 } /* content at t2 (after update) */ article = { id: '5', title: 'The awesome title', content: 'Lorem ipsum dolor sit amet...', likes: 4 } Virtual DOM diff patch
  • 8.
    <ul class="product-menu"> <li data-item-id="65017da4">Productone</li> <li data-item-id="6501816e">Product two</li> </ul> $('.product-menu').on('click', 'li', function () { var id = $(this).data('item-id') loadProduct(id) }) Keeping state in the DOM (slow) <ul class="menu"> <li data-href="index">Home</li> <li data-href="contact">Contact</li> </ul> <div id="page-content"> <!-- content loaded via AJAX --> <form id="content-form"> <!-- ... fields ...--> <button type="button" class="submit"> Contact us </button> </form> </div> $('#content-form .submit').click(function () { var form = $('#content-form') var isValid = validate(form) if (isValid) { postTheForm(form) } }) Where do I fit in?
  • 9.
  • 10.
    When your frameworkis just right: ● Separation of concerns ● Virtual DOM ● In memory state management ● Routing (HTML5 History API) ● Clear coding practices ● AJAX (nice to have)
  • 12.
    Why Vue.js? ● Lightweight ●“True” open-source ● Fastest learning curve ● Separation of concerns in one file ● Great performance per KB * * just invented this metric
  • 13.
    DOM operations performance Source:http://www.stefankrause.net/js-frameworks-benchmark6/webdriver-ts-results/table.html
  • 14.
  • 15.
  • 16.
    Content.vue Footer.vue MenuItem.vue selected Components are ● CustomHTML elements with behaviour attached to them ● Self-contained ● Reside in .vue files (compiled with Webpack) ● Can be mixed with regular HTML ● The Vue application is wrapped in a root component, usually called App.vue
  • 17.
    <template> <div class="content"> <div v-if="loaded"> <h1>{{title }}</h1> <img :src="media" :alt="title"> <p>{{ article }}</p> </div> <div v-else>Loading...</div> </div> </template> <script> export default { name: 'content', data () { return { loaded: false, title: null, article: null, media: null } }, mounted () { axios.get('/latest-news').then(response => { this.title = response.data.title, this.article = response.data.article this.media = response.data.media this.loaded = true }) } } </script> <style> h1 { font-family: "Comic Sans" /* on purpose :) */ } </style> “Reactive” properties Lifecycle events HTML template presentation logic CSS styling Javascript part component logic Content.vue
  • 18.
    <!-- ... --> <divv-if="loaded"> <h1>{{ title }}</h1> <img :src="media" :alt="title"> </div> <!-- ... --> /* ... */ data () { return { loaded: false, title: null, media: null } } /* ... */ show / hide elements fill in HTML elements values for HTML attributes Reactive properties Changing a reactive property triggers a re-render of the associated DOM
  • 19.
    <template> <div class="content"> <div v-if="loaded"> <h1>{{title }}</h1> <p>{{ article }}</p> </div> <div v-else> Loading... </div> </div> </template> <script> export default { name: 'content', data () { return { title: null, article: null } }, computed: { loaded () { return title !== null && article !== null } }, mounted () { // ... } } </script> <style> h1 { font-family: "Comic Sans" /* on purpose :) */ } </style> Computed properties Computed properties: ● Functions that wrap complex logic ● Cached based on their dependencies
  • 20.
    <template> <!-- display article--> <a href="#" v-on:click="toggle()">{{ toggleMessage }}</a> <div v-if="showRelated"> <ul> <li v-for="article in related"> <a href="{{ article.url }}"> {{ article.text }}</a> </li> </ul> </div> </template> <script> export default { name: 'content', data () { return { /* ... */ showRelated: false, related: [ { url: '/path/to/first-article', text: 'First related article' }, { url: '/path/to/second-article', text: 'Second related article' } ] } }, computed: { toggleMessage () { return !this.showRelated ? 'Show related articles' : 'Hide related articles' } }, methods: { toggle() { this.showRelated = !this.showRelated } } } </script> Iterate through properties HTML-like syntax for event handlers Computed properties can be anything
  • 21.
    HTML event handlers!?! ●Easier to locate handlers just by skimming the template ● Easier to test the underlying logic since it’s completely separated from the DOM ● They can be automatically removed together with their associated HTML code
  • 22.
    <template> <header> <ul> <menu-item id="1" label="books"/> <menu-item id="2" label="pens" /> </ul> <header> <div class="main"> <content> </div> <footer> </template> <script> import MenuItem from '/path/to/MenuItem.vue' import Content from '/path/to/Content.vue' import Footer from '/path/to/Footer.vue' export default { name: 'app', components: { 'menu-item': MenuItem, 'content': Content 'footer': Footer } /* ... */ } import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', template: '<App />', components: { App } }) App.vue main.js Components usage
  • 23.
    Parent Child publishevents injectproperties 2 Way datacommunication ● Parents inject values via HTML attributes ● Children push custom events using the v-on:{custom-event} format
  • 24.
    <template> <ul> <menu-item id="1" label="books" v-on:selected="process($event)" /> <menu-item id="2" label="pens" selected="true"" v-on:selected="process($event) /> </ul> </template> <script> importMenuItem from './path/to/MenuItem.vue' export default { name: 'top-menu', components: { 'menu-item': MenuItem }, methods: { process ($event) { // select the category } } } </script> <template> <li :class="{'active-element': selected}"> <a v-on:click="select()"> {{ label }} </a> </li> </template> <script> export default { name: 'MenuItem', props: ['id', 'label', 'selected'], methods: { select () { this.$emit('selected', { id: this.id }) } } } </script> Properties Events App.vue MenuItem.vue
  • 25.
    /* Content.vue */ exportdefault { /* ... */ data () { return { title: null, article: null } }, computed: { loaded () { return article !== null && title !== null } }, props: [ 'articleId' ], watch: { articleId: (value, previous) { axios.get(`/article/{$value}`).then(response => { this.title = response.data.title, this.article = response.data.article }) } } }
  • 26.
    <!-- App.vue --> <template> <ulclass="menu"> <li v-on:click="loadArticle(1)"> First article </li> <li v-on:click="loadArticle(2)"> Second article </li> <!-- ... --> </ul> <content :articleId="articleId" /> </template> <script> /* ... */ export default { data () { return { articleId: null } }, methods: { loadArticle(articleId) { this.articleId = articleId } } } </script> Reactive properties Changes on the articleId are propagated to the child Encapsulated functionality Everything about loading and processing the content is encapsulated
  • 27.
    $ npm installvue-router --save Not this kind of router :) Vue Router Vue Routing ● Provides stateful URLs ● Supports both HTML5 history API as well as the # as fallback
  • 28.
    import Vue from'vue' import Router from 'vue-router' import Homepage from '/path/Home.vue' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Home', component: Homepage } ] }) import Vue from 'vue' import router from '/path/to/router' /* eslint-disable no-new */ new Vue({ el: '#app', router, template: '<App />', components: { App } }) <template> <!-- App.vue --> <router-view /> </template> 1 2 3 router.js main.js App.vue Other features: ● dynamic routes (/article/:id) ● nested routes ● programmatic navigation ● allows adding watchers on routes
  • 29.
    Image from https://vuex.vuejs.org/en/intro.html $npm install vuex --save import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)
  • 30.
    const FETCH_START ='FETCH_START' const FETCH_SUCCESS = 'FETCH_SUCCESS' const FETCH_ERROR = 'FETCH_ERROR' export default new Vuex.Store ({ state: { loading: false, error: null, article: null }, mutations: { [FETCH_START](state) { state.loading = true state.error = null }, [FETCH_SUCCESS](state, article) { state.error = null state.loading = false state.article = article }, [FETCH_ERROR](state) { state.loading = false state.error = true } }, actions: { load({ commit }, { articleId }) { commit(FETCH_START) axios.get(`/articles/${articleId}`) .then(article => commit(FETCH_SUCCESS, article)) .catch(() => commit(FETCH_ERROR)) } } }) Constants for mutation types (common pattern) State variables Mutations (need to be synchronous) Vuexmodule Actions (can be asynchronous & they end with a mutation)
  • 31.
    <template> <ul class="menu"> <li v-on:click="loadArticle(1)"> Firstarticle </li> <li v-on:click="loadArticle(2)"> Second article </li> <!-- ... --> </ul> <content /> </template> <script> import { mapActions } from 'vuex' export default { methods: { ...mapActions({ 'loadArticle': 'load' }), // other component specific methods } } </script> UsingVuex Map Vuex actions in the local scope ( mapAction helper & spread operator) Templates stay the same
  • 32.
    Vuex Menu.vue (update selected item) Content.vue (displaythe content) Breadcrumbs.vue (display the current node) App.vue (update the <title>) import { mapState } from 'vuex' export default { computed: { ...mapState({ loading: state => state.loading, error: state => state.error, article: state => state.article }) }, watch: { article: (value, previous) { // trigger an update } error: (value) { if (value) { // signal an error to the user } } } }
  • 33.
    HTML event handlers!?! ●Efficient DOM manipulation with Virtual DOM ● ● lightweight ● efficient DOM manipulation ● separation of concerns & encapsulation ● routing (HTML5 History API & hash sign) ● state management with Vuex
  • 35.
    $ npm install-g vue-cli $ vue init webpack my-first-vue-project Vue CLI ● Quickly start a new project ● Multiple templates and build systems * ● Sets up linters (AirBNB or Standard) ● Unit tests (karma) ● End to end tests (Nightwatch) * if in doubt, use Webpack
  • 36.
  • 37.
    Resources ● vuejs.org ● router.vuejs.org ●vuex.vuejs.org ● vuejs/awesome-vue
  • 38.