Big Giant Vue Apps
Published: 2019-10-03 18:52:50

---
layout: post
title: Big Giant Vue Apps
published: true
---

I recently took a huge risk and left Netlify to pursue an opportunity of a lifetime to start a startup. Subs is currently in development. You [can read all about it on our Launchrock](http://subshq.launchrock.com/), and sign up for updates.

Subs makes password management seamless by removing the need to constantly enter a master password. I am joining this startup with someone I deeply respect, and I'm already heads-down working hard on this exciting new project. This isn't my first time writing a new product from idea to production, but it's already introducing me to new and interesting challenges.

For this project, the architecture is going to be a bit different from what I've done in the past. Typically, I've used what I know well: a web app that evolves over time into something bigger. Instead of offering a ton of decent features to a few platforms, the plan is to offer a single, well-built, performant major feature to a multitude of platforms — web, mobile, desktop, and browser extensions.

## Architecture and Tooling

The codebase for this startup will be large, using one mono-repo to house eight individual projects covering all of our targeted platforms. We'll also have an additional "common" project to house all of the shared components, services, and stores.

This might seem like a lot in one repo, but there are many benefits to working this way.

I'm putting the building blocks for cross-platform infrastructure in place now, so that future development is as easy as it can be. One thing I've learned: the prototype usually becomes the final code, so do it right the first time. Putting in the building blocks from the beginning allows me to give the major features the attention they need.

The majority of the front end will be driven by Vue and NativeScript. [I've spoken about the benefits of Vue in the past](https://about.gitlab.com/2016/10/20/why-we-chose-vue/). For this project, using the Vue ecosystem to drive the web application, desktop application, and browser extensions affords a level of reusability across the mono-repo and "common" repo, so code won't need to be written twice.

Nativescript with NativeScript-Vue will be used for the iOS and Android applications. While they won't benefit directly from the same shared components, it may let us share a Vuex store across all application instances to handle state, as well as sharing the services that call the API.

Battle-tested libraries are necessary for encryption-related tasks. So, for extra security, I wrote a command-line interface in Python using [execa](https://github.com/sindresorhus/execa) for process execution. This buys us flexibility in the future. If we want to rewrite the CLI in another language like Rust or Go for greater performance, it would be a drop-in replacement.

## Project Structure

![Diagram of Subs' project structure](https://i.imgur.com/z7VpLtz.png)

The diagram might be confusing so let me explain. The project features at least four Vue CLI generated applications: a web application, a desktop application, a browser extension, and the common library.

The `common` library does not have a build step. It is simply imported into projects. When a project using the `common` modules is built, the `common` library gets baked in. I do have to run `yarn link` initially and link it manually, but it's in my `README` for the future. This way, any changes I make to the `common` library immediately benefit from [hot module replacement](https://webpack.js.org/concepts/hot-module-replacement/), and I can see the changes immediately. 

I can use my shared components in the web app like so:

![code screenshot](https://i.imgur.com/833T3dr.png)

What's cool about this (besides the fact that I'm using [Sublime Text](https://www.sublimetext.com/)) is that my components library is empty minus an entry point.

I am importing every single component at once which **does not** take advantage of [tree shaking](https://webpack.js.org/guides/tree-shaking/) as much as it would in other apps. If that was a concern, I'd restructure my exports in my `common` library. In my case, my app is very simple, so I can get away with this.

Each app within `packages` has its own managed `node_modules`. And I have to run `yarn` on each of the eight projects. And I have to run `yarn link subscommon` everywhere I want to use my common library.

Is this a boring solution? It is a marginally longer setup that isn't a big deal to me. I like my setups to be explicit but not tedious. I like less magic and more understanding. The total time to set up the project is about ten minutes. Could it have been three minutes? Maybe.

## Sharing State and Services

I also share the Vuex store and my services. Since Vuex is the only part of my app that uses the services, they simply get imported on their own.

When Vuex initializes, it performs [a recursive injection](https://github.com/vuejs/vuex/blob/dev/src/mixin.js#L22) of the store into all the child components, so you need to make sure it is using the right Vue instance. Here's my shared Vuex store:

    import Vuex from "vuex";
    import menu from "./menu";
    import user from "./user";
    
    export default function store(Vue) {
      Vue.use(Vuex);
      const store = new Vuex.Store({
        modules: {
          menu,
          user,
          ... more stuff
        }
      });
      return store;
    }

I don't import Vue above because I pass Vue into my web app and desktop app like so:

    import Vue from "vue";
    import App from "./App.vue";
    import { store } from "subscommon";
    
    Vue.config.productionTip = false;
    
    new Vue({
      store: store(Vue),
      render: h => h(App)
    }).$mount("#app");

Now Vuex can latch onto the app properly and populate any child components with `$store`.

## The Sum of All Parts

This project structure serves its purpose by allowing me to iterate on the primary application quickly, across various target platforms. 

How quick? I was able to initialize [Vue CLI Electron Builder](https://github.com/nklayman/vue-cli-plugin-electron-builder), import my store, and have an exact running copy of my desktop app in five minutes, with no code changes. 

I was able to get my Chrome extension running almost as quickly too, but admittedly it took me a few hours to understand how Chrome extensions work, so some time was spent reading the docs. Using the [Vue CLI Plugin Browser Extension](https://github.com/adambullmer/vue-cli-plugin-browser-extension) got me up and running very quickly, promising minimal code changes between all extensions. I'll believe it when I see it, but I haven't gotten there yet.

Meanwhile, my desktop app is so damn small and fully functional. Here's the file structure:

![Screenshot of App.vue file](https://i.imgur.com/Udsvbek.png)

I kept `HelloWorld` as the entry point to all my apps because it's the way Vue CLI 3 scaffolds it and it makes sense to me. I'm sure that, at some point, some developers will tell me it's not kosher and to change it. Until then, I'm keeping it.

We hope to get a working proof-of-concept into your hands very soon so we can hear what you think. If you are interested in what I am building and want to know when my beta launches, [then sign up on Launchrock](http://subshq.launchrock.com/) and I'll keep you updated.