Learning Vue.js by Shipping a Feature

The streets outside our office were slushy with leaves. Autumn had arrived and a beautiful tree I often looked at during meetings had begun to shed its yellow leaves. My squad and I were working on a feature that would enable our French customers to have their orders delivered to a collection point for them to pick up. Internally it was dubbed ‘ship2shop’.

We were starting to realise that part of our sign up flow would need a significant overhaul in order to get the work done in the timeframe we agreed to. I was staring at my favourite tree when someone asked: Could Vue solve this for us?

The tree in question, viewable from the South Wing meeting rooms of our Richmond office

Vue is Tails Engineering’s ‘preferred framework’. This means that when new projects require a frontend framework Vue should be chosen to ensure developer fluency across squads. At the time, there was no Vue code live in production. On top of that, we had very little experience with it within our squad.

The designs for ship2shop required more complex state management than our sign up flow code could handle, which would push us past the deadline. Our existing frontend was an in-house jQuery-based framework that worked well for pages with low interactivity. The idea that we could insert a new framework AND finish the feature faster was a strange proposition.

We would save time by building reusable components. Customers choose a collection point when they sign up and also later on when they want to change their collection point. After building the sign up flow components, we were able to slot these components into the account management area with very little integration work.

Another Vue win was providing two-way binding — something our in-house framework lacked.

Two-way binding means:

  1. When properties in the model get updated, so does the UI
  2. When UI elements get updated, the changes get propagated back to the model

Less Pages, More States

We generally get less sign ups every time we introduce a page into our sign up flow. If we can make pages dynamic and quick, we can claw back some of these sign ups that we would otherwise lose. Vue helps us achieve this goal by making the creation and maintenance of these more complex pages more straightforward.

One of our ship2shop pages required the following state management:

  1. The customer enters their address and we fetch a list of shops
  2. Every time a shop is selected, we fetch the shop data and display it
  3. We also toggle a map via the Google Maps API
  4. Once a shop is selected, we fetch a list of possible delivery windows
  5. (There are also dynamic errors the whole way through)

Vue enabled us to manage these states without having to write hard-to-test utility methods.

Collection point selection (with partially translated text because the GIF is from a local environment)

The ideal way to include Vue in an application is to use a build process for precompiling Vue templates. This means that only the Vue runtime needs to be sent to the client alongside the compiled code — saving on page weight as well as computation time in the user’s browser. For a small performance penalty, it’s possible to compile templates in the user’s browser by loading both the compiler and the runtime. We went for the second option so we could plug our feature into existing pages with minimal setup.

Page Speed Concerns

Some customers use old devices and have slow internet connections. We ensure the Tails experience is consistent by only loading libraries at the time they are required. Until we show the location of a collection point on a map, we don’t need the Google Maps SDK loaded on the client.

When the collection points are displayed we auto select the closest one to the customer's postcode and a map is displayed. At this point, we need to load the library and call it with a latitude and longitude pair. Due to a limitation of the library, it’s difficult to move the map object to a different location in the document tree.

We got around this by destroying the old map and instantiating a new map every time a different collection point is selected. We didn’t want to load the library for each new map so we designed a generic function that takes another function as an argument. The argument function is called after loading the library — or instantly if the library already exists in the window object.

// Runs `func` when Google Maps API is loaded or straight away
initMap(func) {
    
    // Return early if the library is already loaded
    if (window.google !== undefined) return func(window.google);

    // After Google's library runs, it calls a function on the
    // window object, which we define here
    const CALLBACK_NAME = 'gmapsCallback';
    window[CALLBACK_NAME] = () => func(window.google);

    // Create a script element which will be loaded and executed
    // when it is appended to the DOM
    const script = document.createElement('script');
    script.async = true;
    script.defer = true;
    script.src = `https://maps.googleapis.com/maps/api/js?key=${geocodeKey}&callback=${CALLBACK_NAME}`;

    document.querySelector('head').appendChild(script);
}

Ship2shop was ultimately a success. Our squad gained experience with Vue, created a maintainable feature, and delivered value to our customers. It went live in France and after one month approximately 1000 people had chosen to have their orders sent to a collection point.

Developing a quick Vue prototype helped us learn how to design the future of our frontend architecture. I fell in love with the technology and its incredible documentation and community. Honeypot recently released a documentary about Vue and how it came to be. It is heartwarming and captures everything that the community stands for.

Tails Engineering is growing! We're hiring friendly engineers for both fullstack and backend roles. Whether you love Vue or think it's a cinema, we want to hear from you. Find out more information on our careers page.