DEV Community

Mastering JS
Mastering JS

Posted on

Our Recommendations for Vue App Architecture in 2022

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 so we don't waste time bikeshedding Webpack configs or discussing "state management" (is that even a thing anymore?).

In other words, we want to spend most of our calories writing v-if and v-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.

  <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>
Enter fullscreen mode Exit fullscreen mode

We prefer to load Vue from a CDN to avoid having to bundle it ourselves. Same with 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.

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'
      }
    ]
  }
};
Enter fullscreen mode Exit fullscreen mode

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 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:

'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
});
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)