How to load Vue Components on Non-SPA sites
Tags: vue, javascript, webdev, components
Published: 2021-02-28 21:48:41

There are times when you want to use Vue components, but the application you're working on is still in the age of jQuery. Is there a way to start using Vue without switching everything to Vue and making a single page application? Can we get all of that beautiful, juicy component action without breaking everything else that's currently on the site? Also, can we add Vue components without having to rewrite the entire website to use Vue?

Luckily, yes. But there are some things that we'll have to do to get there. Those things are:

1. Create Single File Components in Vue
2. Create a loader script that will add your component to a page when you want it
3. Compile the components in a build step as part of your development

## Create Single File Components in Vue

Now, we can create regular ol' Vue SFCs, or single file components. By creating the whole component in one file, we get the flexibility of thinking of our components as one functional unit made up of HTML, JavaScript, and CSS. We can also write components in TypeScript or SASS if we want to. We can also get syntax highlighting since SFCs end up being fundamentally HTML documents. And with VSCode plugins like [Vetur](https://vuejs.github.io/vetur/), development in SFCs has a lot of benefits going for it.

My recommendation would be to create a new folder at the root of your project called `vue`. This file won't be included in your deployment. Think of this as the source code that will be compiled into actual JavaScript before being deployed.

In that folder, create another called `components`. This is where your Vue component files will go. If you want to test this out, create a file called `Hello.vue` in the `components` folder and add this content:

``` html
<template>
  <div>Hello, {{ name }}.</div>
</template>

<script>
export default {
  data() {
    return {
      name: "World",
    };
  },
};
</script>

<style></style>
```

## Create a loader script

The loader script should then be created that will add the component to the webpage. In this, you define that you want the component to be loaded on a certain element. Loader files can be created in a folder called `vue/loaders`. Name this one `helloLoader.js` and have it look like this:

``` javascript
import Vue from "vue";
import Hello from "../components/Hello.vue";

// The following creates a new instance of the component and puts it anywhere
// a element has a data-vue-component="hello" attribute.
document
  .querySelectorAll("[data-vue-component=hello]")
  .forEach((element) => {
    new Vue(Hello).$mount(element);
  });
```

## Compile the components and loaders

This is the biggest step and one that may be very specific to your current project set up. I'm approaching the following with the idea that your project is just an HTML, CSS, and JavaScript project. If you already have a `package.json` or are using NPM for something else, your mileage may vary.

> For the following, you will need to have the [latest Node LTS installed](https://nodejs.org/), at least 14.16 I would say.

We need to add a compilation step to the project. If you already have Webpack for something else, this will be able to fit into that. If you're not using Webpack, I show below how to do that. Otherwise, I found the information on the [Vue loader](https://vue-loader.vuejs.org/guide/#manual-setup) site.

First, install the `vue`, `vue-loader`, and `vue-template-compiler` libraries:

```bash
npm install -D vue vue-loader vue-template-compiler
```

We'll also install webpack, if you don't have it in your project yet:

```bash
npm install -D webpack webpack-cli
```

And then there's, well, other stuff to include. Webpack needs a lot of other tools to build our code, so let's install those:

```bash
npm install -D @babel/core babel-loader css-loader vue-style-loader
```

And then we need to create a configuration file for webpack at `webpack.config.js`:

```js
// webpack.config.js
const { dirname } = require("path");
const VueLoaderPlugin = require("vue-loader/lib/plugin");

module.exports = {
  entry: {
    "favorite-cat": "./vue/loaders/favoriteCatLoader.js",
  },
  output: {
    filename: "bundle.js",
    path: __dirname,
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      },
      // this will apply to both plain `.js` files
      // AND `<script>` blocks in `.vue` files
      {
        test: /\.js$/,
        loader: "babel-loader",
      },
      // this will apply to both plain `.css` files
      // AND `<style>` blocks in `.vue` files
      {
        test: /\.css$/,
        use: ["vue-style-loader", "css-loader"],
      },
    ],
  },
  plugins: [
    // make sure to include the plugin for the magic
    new VueLoaderPlugin(),
  ],
};
```

Whenever you add a new component—and a new loader—add the loader script to the `entry` section above.

And then add a section to your `package.json` to compile these scripts into a `bundle.js` file:

```json
    "scripts": {
        "build": "webpack --config webpack.config.js --mode development"
    },
```

Run that script with `npm run build` and you should see a new `bundle.js` file in your project. You can include that in your HTML head as `<script src="bundle.js" defer></script>`.

## Use the component

Now you just need to add the following HTML tag to see the component in action:

```html
<div data-vue-component="hello"></div>
```

## Is that it?

Yes, at its basics. But a lot depends on your general set up for your project. For instance, Laravel already has a lot of this builder stuff built into Mix, so that will be set up differently. Other frameworks will help or not help with this as they see fit. If you already have Webpack in your project, you may need to modify the config rather than directly replacing it.

But this is the basic set up if you have a straight HTML, JS, and CSS project you'd like to add it to. This is a great way to progressively enhance your existing JavaScript, especially if you're getting into component based JavaScript development and moving away from something like jQuery and massive JavaScript files.

Since it's not a one size fits all, email me at joe (at) jerickson.net if you run into any issues or have a different configuration and want to know how to get this to work.