Polygon Pattern

Building a campaign tool using Vue and Webpack

  • 1 min read

It’s an exciting time to be a front-end developer. The ecosystem is growing fast - REALLY fast. In this blog post, our Senior Front-End Developer, Ben Simpson writes about Graphite's approach, from a hands-on perspective.

by Ben Simpson
Senior Technical Developer

Over 7 years experience as a Frontend Developer.

Published on Thursday 6th July 2017

It’s an exciting time to be a front-end developer. The ecosystem is growing fast - REALLY fast and thanks to tools like Webpack and Babel we can now write modern JavaScript without having to worry about breaking things for outdated browsers. However, the pace of this evolution can be very stressful, it seems almost daily that a new framework is being released. Some love this, others hate it.

So, in the name of balance (and people’s blood pressure), we are trying to build some consistency into the way Graphite approaches front-end development, by searching for a JavaScript framework that we can use for the majority of our projects. Now, personally, I have long been a fan of Ember.js. I like the fact you are directed by convention over configuration, it makes sense for large projects that need consistency. However, I don’t think it works well in every scenario, you’re not always going to want a router, data models, be forced to use a dedicated CLI etc. 

After playing with some other frameworks: Backbone, React, Angular to name a few, we have settled on Vue.js. We were attracted to Vue by its fast rendering performance, less intrusive templating language and large degree of flexibility. What really pushed us over the edge was the Vue Loader. If you are new to Webpack and Vue, this setup is a great place to start. It shouldn’t take you long to realise how powerful this combination can be.

The Brief

Build an application that allows store personnel to register sales in a rapid and secure way. Incorporate capability to provide feedback using an interactive timeline of their progress, allow them to track rewards such as vouchers and be entered into prize draws for even larger rewards.

The Application

Vue single file components have been a dream to work with; the Vue Loader recognizes your .vue file extension, then automatically compiles your templates, functions and styles.

<template>
    <p>{{ greeting }} World!</p>
</template>
//
<script>
export default {
    data() {
        return {
            greeting: 'Hello'
        }
    }
}
</script>
//
<style scoped>
p {
    font-size: 2em;
    text-align: center;
}
</style>

One of my favourite parts of Vue.js has been the ability to dynamically set which components to use via the reserved <component> tag and the is parameter. We can now loop through an array of objects containing component names as keys to generate an entire page! Amazing if you want to use Vue with a CMS.

<template>
    <div>
        <component :is="item.type" :content="item.content" v-for="(item, index) in collection" :key="index"></component>
    </div>
</template>
//
<script>
import * as Components from './components';
//
export default {
    components: Components,
    data() {
        return {
            collection: [{
                type: 'text',
                content: 'Hello World!'
            }, {
                type: 'image',
                content: {
                    src: 'waterfall.png',
                    alt: 'Picture of a waterfall.'
                }
            }]
        }
    }
}
</script>

The application needed to work like an SPA (single page application) and so we added Vue Router. Although I think the before and after route change hooks could do with some improvement, this has been a great plugin to work with. One of the obstacles building this application was that each new sales campaign had the chance to include extra pages; luckily ever since version 2.2.0 Vue Router allows us to add new routes dynamically using the addRoutes method. Problem solved!

Then we have authentication. Unfortunately there seems to be a lack of support in the Vue community for this fundamental piece of logic. We opted for Vue Auth as it seemed to be the only popular plugin for Vue at the time. However, the configuration seems a little messy and in some instances it did not perform as expected. For example we could not get the parseUserData hook to intercept any response data, and so we had to provide a workaround for accessing the logged-in user’s data. It looks as though a ticket has already been raised for this specific issue, so hopefully it will be fixed soon.

The final piece of the puzzle was finding an effective way to pass data between components - you could try and attach data to a parent component or by setting up an event bus, but we needed something more powerful. This is where Vuex comes in - it’s a reactive data layer that we have access to from anywhere within the application. Initially we found Vuex a little strange to work with, properties contained within the state layer need to be submitted to a mutation method before they could be updated, rather than simply writing x = y. The use of Actions did not become clear to us until we started using them as wrappers for promise based Ajax requests. It also took us some time to see the benefits in using Vuex’s built-in object spread operator (ECMA Stage 3) methods, but after writing a few components without them, we soon noticed how verbose the code was becoming.

Before:

<template>
    ...
</template>
//
<script>
export default {
    computed: {
        // state
        userLocation() {
            return this.$store.state.user.location;
        }
    }
    methods: {
        // mutations
        showMenu() {
            this.$store.commit('modal/showModal', 'menu');
        },
        closeMenu() {
            this.$store.commit('modal/closeModal');
        },
        // action
        saleSuccess() {
            this.$store.dispatch('timeline/updateTimeline');
        }
    }
}
</script>

After:

<template>
    ...
</template>
//
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
export default {
    computed: {
        ...mapState('user', [
            'location'
        ]),
        // state
        userLocation() {
            return this.location;
        }
    }
    methods: {
        ...mapMutations('modal', [
           'showModal',
           'closeModal'
        ]),
        ...mapActions('timeline', [
            'updateTimeline'
        ]),
        // mutations
        showMenu() {
            this.showModal('menu');
        },
        closeMenu() {
            this.closeModal();
        },
        // action
        saleSuccess() {
            this.updateTimeline();
        }
    }
}
</script>


The Side effects

  1. Workflow organisation: I don’t know about you, but a single file containing an entire project’s workflow makes me feel a bit sick. Some developers like to separate Gulp tasks into individual files and then import them all back into the gulpfile. When it comes to Webpack we like to use 1 common settings file to serve as a base, then separate development and production configurations which are selectively merged based on the environment.
  2. File and folder structure: this is key for your own (and others) sanity. We have reorganised these files a good handful of times, but at long last we are happy with the layout.

    folder-structure.png?mtime=20170516095632#asset:2983
  3. Styling methodology: decide early on which css styling convention you would like to adopt. We have spent some time getting up to speed on ITCSS in conjunction with BEM-IT and for company-wide consistency we are not about to change that. Unfortunately, it didn’t work well when kept within component files. We ended up trying to manage styles in two primarily separate locations, which for new developers who are not be familiar with the project, might have found somewhat confusing. However, if you would like to go all in with the single file approach, please check out CSS modules, it looks pretty cool.
  4. Eruda: I won’t go into detail here, but this plugin aimed at rapidly debugging mobile devices has proven to be incredibly useful.


The Result

Vue has been a fantastic tool that we have thoroughly enjoyed using. We truly believe we have found a setup that most developers can get on board with and produce high quality applications without the typical steep learning curve that you might expect from other frameworks. Overall, Vue effortlessly handled whatever we could throw at it, instilling our confidence that we have made the right decision.

The community backing is huge and increasing daily. The vast majority of plugins they produce are battle tested and blend beautifully with Vue API in such a way that you’d be surprised it wasn’t part of the original package. They even have their own chrome devtools extension. Being a massive nerd, I find satisfaction in watching the Vuex properties update successfully.... Oh dear!

The platform

Carrot is the second in our family of products helping brands connect with users. Our Prize Media web app has been used by brands such as Disney, Shazam, Uniqlo, Samsung, Klipsch & Clarins in markets all around the world. Carrot extends this idea and allows us to run fully featured incentivised campaigns for sales teams, without the need for a huge IT integration project. You can read more about Carrot here.