Vue.js is the new hotness in the front-end world, garnering more github stars than any other project in 2017. It makes sense - Vue is simpler and easier to learn than frameworks like React.js and Angular, while still providing all the power those frameworks do. And it's easy to start using it incrementally, just dropping it into place within a site without having to change your entire site over to a single page app.

I had a recent project where I had a mostly static site built using the ZURB template, but where I wanted to add some more complex elements using Vue.js. Vue makes it relatively straightforward to "just drop in" the Vue JavaScript and get started, but I wanted to integrate Vue fully into my build system, use Vue single-file components, and transparently be able to put custom Vue-based elements directly into an otherwise static site. Something like this:

<div>
  <my-custom-element></my-custom-element>
</div>

This turns out to not be too difficult, but it took a little figuring out so I thought I'd put together a quick tutorial in case anyone else wants to do this.

Dependencies

To start with, we need to install a few dependencies. We'll want to include vue of course, and I used a library called Vue Custom Element to let me use vue elements directly in the page.

npm install --save vue vue-custom-element

The `vue-custom-element` library relies on the W3C Custom Elements specification, which as of this writing is only available on Chrome, Safari, and a couple smaller mobile browsers according to caniuse. To enable it across all browsers we should also install a polyfill:

npm install --save document-register-element

Build Dependencies

Next, in order to integrate Vue single file components directly into our build system with webpack, we're going to need some dev dependencies. We'll set up vue-loader and vue-template-compiler, along with their peer dependency css-loader.

npm install --save-dev vue-loader css-loader vue-template-compiler

Build

To teach our build system how to watch single file components, we need to update our webpack configuration to understand .vue files. The ZURB stack's default build system includes its webpack configuration in its gulpfile, so we need to modify that.

The modifications we need to make are

  1. Add .vue files to resolve, so that webpack knows to find and compile .vue files.
  2. Add a .vue rule that tells webpack how to compile .vue files.
  3. (Bonus) add .vue files to watch down further in the gulpfile so we get automatic recompilation on changes.

The webpack configuration in gulpfile.js ends up looking like this (assuming no other changes):

let webpackConfig = {
  resolve: {
    extensions: ['.js', '.vue', '.json'],
  },
  module: {
    rules: [
      {
        test: /.js$/,
        use: [
          {
            loader: 'babel-loader',
          }
        ],
      },
      {
        test: /.vue$/,
        use: [
          {
            loader: 'vue-loader'
          }
        ]
      }
    ]
  }
}

If you also want to add the additional watch line, that changes the watch function to look like this:

function watch() {
  gulp.watch(PATHS.assets, copy);
  gulp.watch('src/pages/**/*.html').on('all', gulp.series(pages, browser.reload));
  gulp.watch('src/{layouts,partials}/**/*.html').on('all', gulp.series(resetPages, pages, browser.reload));
  gulp.watch('src/assets/scss/**/*.scss').on('all', sass);
  gulp.watch('src/assets/js/**/*.js').on('all', gulp.series(javascript, browser.reload));
  gulp.watch('src/assets/js/**/*.vue').on('all', gulp.series(javascript, browser.reload));
  gulp.watch('src/assets/img/**/*').on('all', gulp.series(images, browser.reload));
  gulp.watch('src/styleguide/**').on('all', gulp.series(styleGuide, browser.reload));
}

Importing Vue Elements

Now we have all the pieces in place to begin importing and using Vue-based elements in our site. The final step is to import them into our JavaScript, and register them so that on page load inline Vue elements will be initialized.

To do this we need to import `vue` and `vue-custom-element`, import any elements we've implemented, and then register them with each of these libraries. For simplicity, we can just do this directly in our app.js file:

import Vue from 'vue'
import vueCustomElement from 'vue-custom-element'

import MyCustomElement from './my-custom-element';

Vue.use(vueCustomElement);

Vue.customElement('my-custom-element', MyCustomElement)

Now in your pages, you can use these elements just like any other element:

<div>
  <my-custom-element></my-custom-element>
</div>

If you'd like to see a working example, I've set up a barebones repository with a very simple custom element here: https://github.com/kball/example-zurb-stack-vue