Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Adding Vue to an existing stack and get ready to scale

Roman Kuba
February 16, 2018

Adding Vue to an existing stack and get ready to scale

Greenfield projects are a luxury you very often don't have. In this talk, we will look at how we accomplish the task of adding Vue to an existing codebase while avoiding the pitfalls and get to a scale ready state.

Roman Kuba

February 16, 2018
Tweet

More Decks by Roman Kuba

Other Decks in Programming

Transcript

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  4. BUT NECESSARY
    Photo by Glenn Carstens-Peters on Unsplash

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  10. 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

    View full-size slide

  11. YOU TEAM
    Photo by Sweet Ice Cream Photography on Unsplash

    View full-size slide

  12. SPLIT THE PROCESS
    INTO PHASES

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  15. 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)
    )
    })
    })

    View full-size slide

  16. 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)
    )
    })
    })

    View full-size slide

  17. 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)
    )
    })
    })

    View full-size slide

  18. INTRODUCE VUE
    PHASE 2
    Photo by Clem Onojeghuo on Unsplash

    View full-size slide

  19. 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

    View full-size slide

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

    View full-size slide

  21. !
    HTML
    LEVERAGE VUE
    LIFECYCLE

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  24. WEBPACK IS YOUR
    FRIEND

    View full-size slide

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

    View full-size slide



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

    View full-size slide

  27. ARE YOU READY
    Photo by Geran de Klerk on Unsplash

    View full-size slide

  28. BUILD A SPA
    PHASE 4
    Photo by Clem Onojeghuo on Unsplash

    View full-size slide

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

    View full-size slide

  30. APP
    SPA SPA SPA SPA
    TREAT PAGES AS 

    SEPARATE SPAS

    View full-size slide

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

    View full-size slide

  32. ADD VUEX
    Photo by Samuel Zeller on Unsplash

    View full-size slide

  33. APP
    JS
    PACK
    N
    V
    PACK
    PACK

    View full-size slide

  34. head
    body
    .content
    = vue_app :users
    SLIM

    !



    !
    !
    PHP

    View full-size slide

  35. head
    body
    .content
    = vue_app :users
    SLIM

    !



    !
    !
    PHP

    View full-size slide

  36. head
    body
    .content
    = vue_app :users
    SLIM

    !



    !
    !
    PHP
    users-pack

    View full-size slide

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

    View full-size slide

  38. head
    body
    .content
    = vue_app :users,
    users: @users.to_json,
    role: current_user.role
    SLIM
    vue-app="users" 

    data-users="!!..." 

    data-role="admin">!

    View full-size slide

  39. head
    body
    .content
    = vue_app :users,
    users: @users.to_json,
    role: current_user.role
    SLIM
    vue-app="users" 

    data-users="!!..." 

    data-role="admin">!
    1. MOUNT VUE
    2. ADD STORE
    3. PASS DATA

    View full-size slide

  40. 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

    View full-size slide

  41. 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

    View full-size slide

  42. 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

    View full-size slide

  43. 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

    View full-size slide

  44. 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)
    }

    View full-size slide

  45. JS
    import { app } from '!../lib/app'
    import Store from '!../apps/users/store'
    import Users from '!../apps/users'
    app('users', Users, Store)
    VUE


    !
    !
    <br/>import User from './user'<br/>export default {<br/>name: 'Users',<br/>props: ['users', 'role'],<br/>computed: {<br/>canEdit() {<br/>return this.role !!=== 'admin'<br/>}<br/>}<br/>}<br/>!

    View full-size slide

  46. JS
    import { app } from '!../lib/app'
    import Store from '!../apps/users/store'
    import Users from '!../apps/users'
    app('users', Users, Store)
    VUE


    !
    !
    <br/>import User from './user'<br/>export default {<br/>name: 'Users',<br/>props: ['users', 'role'],<br/>computed: {<br/>canEdit() {<br/>return this.role !!=== 'admin'<br/>}<br/>}<br/>}<br/>!

    View full-size slide

  47. GET READY TO SCALE
    PHASE 5

    View full-size slide

  48. APP
    SPA SPA SPA SPA

    View full-size slide

  49. APP
    SPA SPA SPA SPA
    PACK PACK PACK

    View full-size slide

  50. SPA SPA SPA SPA
    PACK PACK PACK

    View full-size slide

  51. PREPARE
    SCAFFOLDING
    AND GENERATORS
    Photo by Rachael Gorjestani on Unsplash

    View full-size slide

  52. Photo by Rachael Gorjestani on Unsplash

    View full-size slide

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

    View full-size slide

  54. GOOD TIMES AHEAD
    CONCLUSION

    View full-size slide

  55. Acceptance Specs Unit Specs

    View full-size slide

  56. feature
    feature feature

    View full-size slide

  57. VUE MADE IT ENJOYABLE
    Photo by Yvette de Wit on Unsplash

    View full-size slide

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

    View full-size slide

  59. THANKS
    Photo by Yvette de Wit on Unsplash

    View full-size slide