Slide 1

Slide 1 text

Photo by ⻉贝莉⼉儿 NG on Unsplash SCALING VUE IN AN EXISTING STACK

Slide 2

Slide 2 text

IT’S NOT ALWAYS A GREENFIELD PROJECT Photo by Dawid Zawiła on Unsplash

Slide 3

Slide 3 text

ADDING NEW TECH IS ALWAYS AN INVESTMENT Photo by Glenn Carstens-Peters on Unsplash

Slide 4

Slide 4 text

BUT NECESSARY Photo by Glenn Carstens-Peters on Unsplash

Slide 5

Slide 5 text

COMPETING TECH WILL RUN IN PARALLEL Photo by David Marcu on Unsplash

Slide 6

Slide 6 text

WHERE TO DRAW THE LINE? Photo by Mariusz Prusaczyk on Unsplash

Slide 7

Slide 7 text

A FULL SPA IS PROBABLY NOT POSSIBLE AT ALL Photo by Mariusz Prusaczyk on Unsplash

Slide 8

Slide 8 text

HI MY NAME IS ROMAN KUBA SEN. SOFTWARE ENGINEER Photo by Mauro Licul on Unsplash @CODEBRYO @CODESHIP

Slide 9

Slide 9 text

BEEN THERE, DONE THAT. Photo by Joanna Kosinska on Unsplash

Slide 10

Slide 10 text

BEEN THERE, DONE THAT. Photo by Joanna Kosinska on Unsplash ‣ Stack dictated by jQuery, Angular and CoffeeScript ‣ Not a lot of resources ‣ Large amount of users ‣ A very backend heavy system

Slide 11

Slide 11 text

YOU TEAM

Slide 12

Slide 12 text

YOU TEAM Photo by Sweet Ice Cream Photography on Unsplash

Slide 13

Slide 13 text

SPLIT THE PROCESS INTO PHASES

Slide 14

Slide 14 text

REDUCE ALL THE THINGS PHASE 1 Photo by Brian Yu on Unsplash

Slide 15

Slide 15 text

REDUCE Photo by Joanna Kosinska on Unsplash ‣ Decide on one language ‣ Remove unknowns ‣ Maybe refactor little bits and pieces

Slide 16

Slide 16 text

Coffee $ !-> $('a.ajax').on 'click', (e) !-> e.prenventDefault() $.get($(this).attr('href')) .success (response) !-> console.log(response) $(function() { $('a.ajax').on('click', function(e) { e.preventDefault() $.get($(this).attr('href')) .success(function(response) { console.log(response) }) }) }) JS JS const nodes = document.querySelectorAll('a.ajax') Array.from(nodes).forEach( a !=> { a.addEventListener('click', (e) !=> { e.preventDefault() axios.get(a.href) .then(response !=> console.log(response) ) }) })

Slide 17

Slide 17 text

Coffee $ !-> $('a.ajax').on 'click', (e) !-> e.prenventDefault() $.get($(this).attr('href')) .success (response) !-> console.log(response) $(function() { $('a.ajax').on('click', function(e) { e.preventDefault() $.get($(this).attr('href')) .success(function(response) { console.log(response) }) }) }) JS JS const nodes = document.querySelectorAll('a.ajax') Array.from(nodes).forEach( a !=> { a.addEventListener('click', (e) !=> { e.preventDefault() axios.get(a.href) .then(response !=> console.log(response) ) }) })

Slide 18

Slide 18 text

Coffee $ !-> $('a.ajax').on 'click', (e) !-> e.prenventDefault() $.get($(this).attr('href')) .success (response) !-> console.log(response) $(function() { $('a.ajax').on('click', function(e) { e.preventDefault() $.get($(this).attr('href')) .success(function(response) { console.log(response) }) }) }) JS JS const nodes = document.querySelectorAll('a.ajax') Array.from(nodes).forEach( a !=> { a.addEventListener('click', (e) !=> { e.preventDefault() fetch(a.href) .then(response !=> console.log(response) ) }) })

Slide 19

Slide 19 text

INTRODUCE VUE PHASE 2 Photo by Clem Onojeghuo on Unsplash

Slide 20

Slide 20 text

PHASE 2 INTRODUCE VUE ▸ Choose a manageable target ▸ Define one clear entry point for Vue ▸ Load Vue from a CDN ▸ Use what Vue offers out of the box

Slide 21

Slide 21 text

!
HTML

Slide 22

Slide 22 text

!
! const vm = new Vue({ el: '[vue="feature"]', data() { return { status: 'loading' } }, template: ` <div> <span class="status">{{ status }}!</span> !</div> `, }) ! HTML

Slide 23

Slide 23 text

!
HTML

Slide 24

Slide 24 text

!
HTML LEVERAGE VUE LIFECYCLE

Slide 25

Slide 25 text

!
const vm = new Vue({ el: '[vue="feature"]', data() { return { url: null, status: 'loading' } }, template: `…`, methods: { fetchStatus() { !// !!... } }, beforeMount() { this.url = this.$el.dataset.url this.fetchStatus() } }) ! HTML

Slide 26

Slide 26 text

BETTER BUILD-PROCESS PHASE 3 Photo by Clem Onojeghuo on Unsplash

Slide 27

Slide 27 text

WEBPACK IS YOUR FRIEND

Slide 28

Slide 28 text

APP JS JS V N

Slide 29

Slide 29 text

APP JS JS V N

Slide 30

Slide 30 text

APP JS JS N V

Slide 31

Slide 31 text

PHASE3 BETTER BUILD-PROCESS ▸ Aim for clean separation (No Globals, No legacy) ▸ Perfect chance to introduce Vue SFC

Slide 32

Slide 32 text

{{ status }}! !
! export default { props: { url: String }, data() { return { status: 'loading' } }, methods: { fetchStatus() { !// !!... } }, created() { this.fetchStatus() } } ! VUE

Slide 33

Slide 33 text

ARE YOU READY Photo by Geran de Klerk on Unsplash

Slide 34

Slide 34 text

BUILD A SPA PHASE 4 Photo by Clem Onojeghuo on Unsplash

Slide 35

Slide 35 text

DIDN’T YOU SAY AN SPA DOESN’T MAKE SENSE? Photo by Mariusz Prusaczyk on Unsplash

Slide 36

Slide 36 text

APP SPA

Slide 37

Slide 37 text

APP SPA

Slide 38

Slide 38 text

APP SPA SPA SPA SPA TREAT PAGES AS 
 SEPARATE SPAS

Slide 39

Slide 39 text

PERFECT TIME TO GO ALL IN ON THE VUE ECOSYSTEM AND IT’S POWERS Photo by Glenn Carstens-Peters on Unsplash

Slide 40

Slide 40 text

ADD VUEX Photo by Samuel Zeller on Unsplash

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

APP JS JS N V

Slide 43

Slide 43 text

APP JS JS N V

Slide 44

Slide 44 text

APP JS PACK N V PACK PACK

Slide 45

Slide 45 text

head body .content = vue_app :users SLIM !
!
! PHP

Slide 46

Slide 46 text

head body .content = vue_app :users SLIM !
!
! PHP

Slide 47

Slide 47 text

head body .content = vue_app :users SLIM !
!
! PHP users-pack

Slide 48

Slide 48 text

head body .content = vue_app :users SLIM
!

Slide 49

Slide 49 text

head body .content = vue_app :users, users: @users.to_json, role: current_user.role SLIM
!

Slide 50

Slide 50 text

head body .content = vue_app :users, users: @users.to_json, role: current_user.role SLIM
!
1. MOUNT VUE 2. ADD STORE 3. PASS DATA

Slide 51

Slide 51 text

JS import Vue from 'vue' import Vuex from 'vuex' import { entries, merge } from 'lodash' export function app(name, component, customStore = {}) { Vue.use(Vuex) const node = document.querySelector(`#vue-app[vue-app=${name}]`) !// Basic Root store const defaultStore = { state: { !!... }, getters: { !!... }, plugins: [!!...] } const store = merge({}, defaultStore, customStore) !// Read and pass props into the main coponent const props = {} entries(node.dataset).forEach(([key, value]) !=> { try { props[key] = JSON.parse(value) } catch (e) { props[key] = value } }) !// Basic Root instance

Slide 52

Slide 52 text

JS import Vue from 'vue' import Vuex from 'vuex' import { entries, merge } from 'lodash' export function app(name, component, customStore = {}) { Vue.use(Vuex) const node = document.querySelector(`#vue-app[vue-app=${name}]`) !// Basic Root store const defaultStore = { state: { !!... }, getters: { !!... }, plugins: [!!...] } const store = merge({}, defaultStore, customStore) !// Read and pass props into the main coponent const props = {} entries(node.dataset).forEach(([key, value]) !=> { try { props[key] = JSON.parse(value) } catch (e) { props[key] = value } }) !// Basic Root instance

Slide 53

Slide 53 text

JS import Vue from 'vue' import Vuex from 'vuex' import { entries, merge } from 'lodash' export function app(name, component, customStore = {}) { Vue.use(Vuex) const node = document.querySelector(`#vue-app[vue-app=${name}]`) !// Basic Root store const defaultStore = { state: { !!... }, getters: { !!... }, plugins: [!!...] } const store = merge({}, defaultStore, customStore) !// Read and pass props into the main coponent const props = {} entries(node.dataset).forEach(([key, value]) !=> { try { props[key] = JSON.parse(value) } catch (e) { props[key] = value } }) !// Basic Root instance

Slide 54

Slide 54 text

JS import Vue from 'vue' import Vuex from 'vuex' import { entries, merge } from 'lodash' export function app(name, component, customStore = {}) { Vue.use(Vuex) const node = document.querySelector(`#vue-app[vue-app=${name}]`) !// Basic Root store const defaultStore = { state: { !!... }, getters: { !!... }, plugins: [!!...] } const store = merge({}, defaultStore, customStore) !// Read and pass props into the main coponent const props = {} entries(node.dataset).forEach(([key, value]) !=> { try { props[key] = JSON.parse(value) } catch (e) { props[key] = value } }) !// Basic Root instance

Slide 55

Slide 55 text

JS entries(node.dataset).forEach(([key, value]) !=> { try { props[key] = JSON.parse(value) } catch (e) { props[key] = value } }) !// Basic Root instance const instance = { name: 'App', store: new Vuex.Store(store), render(h) { return h( 'div', { attrs: { id: `app-${name}`, }, }, [h(component, { props })] ) }, } return new Vue(instance).$mount(node) }

Slide 56

Slide 56 text

JS import { app } from '!../lib/app' import Store from '!../apps/users/store' import Users from '!../apps/users' app('users', Users, Store) VUE
    !
! import User from './user' export default { name: 'Users', props: ['users', 'role'], computed: { canEdit() { return this.role !!=== 'admin' } } } !

Slide 57

Slide 57 text

JS import { app } from '!../lib/app' import Store from '!../apps/users/store' import Users from '!../apps/users' app('users', Users, Store) VUE
    !
! import User from './user' export default { name: 'Users', props: ['users', 'role'], computed: { canEdit() { return this.role !!=== 'admin' } } } !

Slide 58

Slide 58 text

GET READY TO SCALE PHASE 5

Slide 59

Slide 59 text

APP SPA SPA SPA SPA

Slide 60

Slide 60 text

APP SPA SPA SPA SPA PACK PACK PACK

Slide 61

Slide 61 text

SPA SPA SPA SPA PACK PACK PACK

Slide 62

Slide 62 text

PREPARE SCAFFOLDING AND GENERATORS Photo by Rachael Gorjestani on Unsplash

Slide 63

Slide 63 text

Photo by Rachael Gorjestani on Unsplash

Slide 64

Slide 64 text

BACK IT UP BY PATTERNS AND SPECS Photo by Rachael Gorjestani on Unsplash

Slide 65

Slide 65 text

GOOD TIMES AHEAD CONCLUSION

Slide 66

Slide 66 text

Acceptance Specs Unit Specs

Slide 67

Slide 67 text

2s 0,25s

Slide 68

Slide 68 text

feature feature feature

Slide 69

Slide 69 text

VUE MADE IT ENJOYABLE Photo by Yvette de Wit on Unsplash

Slide 70

Slide 70 text

VUE MADE IT ENJOYABLE … BECAUSE IT’S JUST JS DONE RIGHT Photo by Yvette de Wit on Unsplash

Slide 71

Slide 71 text

THANKS Photo by Yvette de Wit on Unsplash