Wilcox Development Solutions Blog

Single file web components on legacy projects

August 01, 2016

I’m constantly looking for new innovations that I can use on brown-field projects. You know the ones: you come in and there’s a large engineering investment in a project, so architectural decisions have been made, and to change those decisions would require large amounts of capital (both political and money capital).

So we have legacy code. But we can still learn from other modern, greefield projects, and apply new concepts from front end frameworks into our code.

We’ll examine one of this new front end ideas here: Single File Components.

Single Files Components: The State of the Art in Javascript Land

Traditionally CSS, HTML and Javascript have been in separate files. Separate languages, separate files. React.js and Vue.js have an interesting idea: that often these pieces interact with each other at a component level - such that having the three different pieces in the same file makes sense.

For example, you’ve been given a task to implement a toolbar. Normally you create a couple CSS classes, put them in a CSS file. Create an HTML structure, which maybe you put in a template somewhere . Documentation… somewhere. Some Javascript to tie it together, in a forth place.

But what if everything lived in the same file, instead of being spread out? Because ultimately, you’re building a toolbar - a single piece of UI.

React.js solves this with JSX - a Javascript preprocessor that lets you splat HTML in the middle of your Javascript. JSX is somewhat interesting, but assumes you buy into (the DOM building parts of) React.

React is pretty cool, and - in my mind - is easier to integrate with pre-existing sites than say Angular. If you want a new, complicated, individual component on your site, maybe React is worth looking into.

But Vue.JS has another solution to the “let’s keep stuff about a single thing together”.

Vue.JS solves this with Single File Components.

Vue.js Component files take cues from HTML: Javascript lives in a script tag, CSS lives in a style tag, HTML templates live in a template tag. This might remind you of your first web pages, before you worried about separating out everything (for your one page/one file “HI MOM” page with the blink tags…).

Bringing the state of the art back home, to legacy-ville

With a little help from Gulp and Gulp VueSplit we can split out Vue Component files into their individual files.

The awesome thing about Gulp-Vuesplit is that it’s a simple text extraction: pulling out the different parts of a component file then writing them out to separate files.

Let’s take a look at a simple component:

// components/clicker.vue
function autoClickers() {
    var clickers = [].slice.call(document.querySelectorAll('.clicker-clickers')) // turn into iterable array

    clickers.forEach(function(current) {
          current.addEventListener('click', function() { alert( current.innerHTML ) }, false);
    } )
document.addEventListener( 'DOMContentLoaded', autoClickers )

.clickers {
    text-transform: uppercase;

We can split this file up with a simple Gulpfile:

var vueSplit = require( 'gulp-vuesplit' )

var gulp = require( 'gulp' )
var pipe = require( 'gulp-pipe' )
var vueSplit = require( 'gulp-vuesplit' ).default

gulp.task('vuesplit', function() {
    return gulp.src('components/*.vue').
        pipe( vueSplit( { cssFilenameScoped: true } ) ).
        pipe( gulp.dest("dist/"))

This generates two files - one for the JS and one for the CSS. (It would also generate a file for anything in a template tag, but we don’t have one here).

The generated JS is nothing remarkable - it’s just everything in the script tag, but the generated CSS is:

.clicker-clickers {
    text-transform: uppercase;

Notice the .clicker- prefix of our CSS class? That was generated by Gulp-VueSplit. It performs a kind of auto name-spacing, either with the file name prefixed, or with a unique hash suffix.

Now that we’ve generated seperate files, we can use them in an HTML page - even a simple HTML with no extra Javascript frameworks!

    <link rel="stylesheet" type="text/css" href="dist/clicker.css" />
    <script src="dist/clicker.js"></script>

    <p class="clicker-clickers">Hello world</p>
    <p>Not clickable</p>