Our Recommendations for Vue App Architecture in 2022
Published: 2022-04-07 15:55:31

At Mastering JS, we're all about pragmatic web development. That means we aggressively avoid work that isn't related to shipping new features. We believe in spending [at most 1 hour per quarter on build system work](https://www.getrevue.co/profile/masteringjs/issues/why-you-shouldn-t-use-a-static-site-generator-for-your-next-blog-292833) so we don't waste time bikeshedding [Webpack configs](https://masteringjs.io/tutorials/webpack/config) or discussing "state management" (is that even a thing anymore?).

In other words, we want to spend most of our calories writing [v-if](https://masteringjs.io/tutorials/vue/v-if) and [v-bind](https://masteringjs.io/tutorials/vue/bind) statements, not tinkering with our build.

## Overview

Everything starts with an `index.html` file that we typically host on Netlify. The `index.html` file contains the below `body`.

```html
  <body>
    <div id="content"></div>

    <script src="https://unpkg.com/vue@3.x"></script>
    <script src="https://unpkg.com/vue-router@4.0.10"></script>
    <script src="/bundle.js"></script>
  </body>
```

We prefer to load [Vue from a CDN](https://masteringjs.io/tutorials/vue/cdn) to avoid having to bundle it ourselves. Same with [Vue Router](https://masteringjs.io/tutorials/vue/router). Why? Tree shaking isn't worth it for us, because we don't put Vue on SEO-focused pages. If we want a page to rank on Google, we do vanilla JS, _unless_ the page is specifically about Vue and needs to have some interactive examples.

Our bundle does contain most other dependencies, which is why we build our bundle using Webpack.

## Webpack Config

Below is our standard Webpack config for Vue projects.

```javascript
const config = require('./.config');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: {
    app: `${__dirname}/src/main.js`
  },
  target: 'web',
  output: {
    path: `${__dirname}/public`,
    filename: 'bundle.js'
  },
  optimization: {
    minimize: false // <-- Remove need for source maps
  },
  plugins: [
    // Allow accessing config
    ...Object.keys(config).map(key => {
      return new webpack.DefinePlugin({ [`__${key}`]: config[key] });
    })
  ],
  module: {
    rules: [
      {
        test: /\.html$/i, // <-- Allow `require()` on HTML files
        type: 'asset/source'
      },
      {
        test: /\.css$/i, // <-- Allow `require()` on CSS files
        type: 'asset/source'
      }
    ]
  }
};
```

3 things worth noting about this config:

1. We disable minification in prod. Again, the performance benefit of minifying is so negligible in our case that it isn't worth the developer time to set up source maps, etc. If minification provides a meaningful performance benefit, that means you're shipping wayyyyyyyyyy too much JavaScript and should fix your app.
2. Use [plugins](https://masteringjs.io/tutorials/webpack/define-plugin) to import config.
3. Import HTML and CSS as strings using `type: 'asset/source'`.

Why do we import HTML and CSS as strings? So we can define the HTML and CSS for our components in `.html` and `.css` files. For example, below is our `footer/index.js` file:

```javascript
'use strict';

const appendCSS = require('../appendCSS');
const css = require('./footer.css');
const template = require('./footer.html');

appendCSS(css);

module.exports = app => app.component('footer-component', {
  template: template
});
```

This means all our layout work is done in HTML and CSS. No need to actually touch JavaScript. A huge win for us, because this means we can delegate most of our new layout work out to designers on Fiverr or similar services. They send us a complete layout, and we just put it into a component.

## State

State management is largely a non-issue in Vue. Frontend devs tend to discuss state management ad infinitum because it is a huge problem in frameworks like React, which are written for pre-ES6 JavaScript and are hopelessly out of date.

For us, our top-level page components (the components we pass to Vue Router) are responsible for setting state and fetching data. To avoid prop drilling, they pass state down using [Vue's `provide` option](https://masteringjs.io/tutorials/vue/inject).