Vue Webpack Logos

Building a campaign tool using Vue and Webpack

  • 10 mins read
by Ben Simpson
Senior Technical Developer

Over 6 years experience as a Full-Stack developer.

Published on Tuesday 16th May 2017

It’s a very exciting time to be a frontend developer. The ecosystem is growing fast, REALLY fast and thanks to tools like Webpack and Babel we can now write modern ES6 JavaScript. These advancements are exactly what JavaScript has needed for a very long time, but the speed of this growth can be stressful and it doesn’t suit everyone. I have spoken to a quite few developers that have chosen server-side development for its maturity and pace of evolution. I only have to mention that Google’s Angular has reached its 4th major release to see them literally fidget with discomfort. I on the other hand, love it. I eagerly wait for the next JavaScript Weekly to hit my inbox, just so that I can scan it for new libraries, tools and anything that will help me level up.

So, in the name of balance (and people’s blood pressure), I am trying to build some consistency into the way Graphite approaches frontend 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 that you are directed by convention over configuration and I believe it can work well for large teams, building large projects. 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. etc... 

After playing with some other frameworks: Backbone, React, Angular to name a few, I have settled on Vue.js. I was attracted to Vue by its fast rendering performance, less intrusive templating language and large degree of flexibility. What really pushed me over the edge was the Vue Loader. I love this thing! If you are new to Webpack and Vue then using this setup is a great place to start. It shouldn’t take you long to realise just 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 this meant we needed to add 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. We opted for  Vue Auth as it seemed to be the only plugin we could find at the time that was actually getting some use. 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 opened 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 I 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 me until I started using them as wrappers for promise based Ajax requests, firing the relevant commits (mutations) and then progressing through the applications journey makes it all feel very natural.  It also took me some time to see the benefit in using Vuex’s built-in object spread operator (ECMA Stage 3) methods, but after writing a few components without them I soon noticed how cluttered my code started to look... I was new to the object spread operator and I still think they look a little odd nested within an object but they really do help to clean up the codebase.

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 lessons I’ve learnt

  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 separate files and then import them all back into their gulpfile. When it comes to Webpack I 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, I’ve moved my files around a good handful of times but I am finally happy with the layout, there is something quite satisfying about finding your own convention. folder-structure.png?mtime=20170516095632#asset:2983
  3. Styling methodology: decide early on which css styling convention you would like to adopt, I have spent some time getting up to speed with ITCSS and BEM-IT and for company-wide consistency I’m not about to change that. Unfortunately it didn’t work well for me when kept within component files. I ended up trying to manage my styles in 2 primarily separate locations, which I think for other developers who are not be familiar with the project, might have found quite confusing. I’m also not really sure if I like the idea of bloating out my files with roughly 150 lines of styling, so for now I have decided to keep them separate rather than going all in with the single file approach. However, if you do want to go all in please check out CSS modules, it looks pretty cool.
  4. Eruda ftw: I won’t go into too much detail here but, this plugin aimed at rapidly debugging mobile devices was really useful, and with Webpacks ability to conditionally load scripts we can easily keep it separate from the main bundles.

The Result

Vue has been a fantastic tool that I have thoroughly enjoyed using. I 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 my confidence that we have made the right decision.

The community backing is huge, vibrant and growing still. The vast majority of plugins they produce have been battle tested and blend beautifully with Vue 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 :-/

Now that Graphite has successfully built this fully decked out dynamic SPA, I am excited by the possibility of moving forward with progressive enhancements such as push notifications and offline first capabilities. With the web moving towards a more app like experience, it is becoming increasingly important to learn and understand these key new features. In a few years, the good ol web browser could rule supreme and do away with the need for native mobile applications entirely!

The platform

The Carrot Incentives platform is a great tool for giving brands the power to encourage sales through a unique reward system. The advantages of using a system like this allows brands to have an edge over their competitors and the ability to track sales in real time.