Vue components without .vue files

Playing around with the Parcel bundler, I started thinking about the syntactic sugar that often comes with reactive component frameworks.

If you’re a React user, you may have never written anything that wasn’t JSX.

hello.jsx
1
2
3
4
5
6
7
8
9
10
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}

ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
);

There’s a good reason for that. Non-JSX React is hellish.

React hello.js
1
2
3
4
5
6
7
8
9
10
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}

ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);

Vue does something similar with its single-file components, which contain the HTML template, the Vue JSON object, and optionally scoped or global CSS.

hello.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>Hello {{ toWhat }}!</div>
</template>

<script>
export default {
name: 'Hello',
data () {
return {
toWhat: 'World'
}
}
}
</script>

<style scoped>
div {
font-size: 2em;
text-align: center;
}
</style>

This syntactic sugar is there to help, and it does. It also creates some problems.

  • Your code editor will need help figuring out what to do with a .jsx or .vue file, because they aren’t real things. By not real I mean a JavaScript interpreter will barf on them.
  • Your linting tools will similarly struggle.
  • Your build process just became more complicated. It has to translate your syntactic sugar in to the stuff we feed web browsers. It may have to transpile some elements of your component (ES2015 or SASS or PostCSS etc.) before it transpiles the entire component, where it joins the rest of your standard code, where it will probably get transpiled again. The tooling and configuration for this can be unpleasant.

For React…totally worth it. I wouldn’t do React with JSX. For Vue, I’m not so sure. Take a look at this.

Vue hello.js
1
2
3
4
5
6
7
8
9
10
11
12
13
const template = `
<div class="component-hello">Hello {{ toWhat }}!</div>
`;

export default {
name: 'Hello',
template: template,
data () {
return {
toWhat: 'World'
}
}
};

This is the Vue component in plain JavaScript. I’m using an ES2015 template string to contain the template. I don’t find it any less readable than a single-file component. Exporting the Vue JSON object is exactly the same. No special handling necessary.

You do have to put the CSS in a separate file and import it in your main CSS file. But there are advantages to that. CSS in single-file Vue components doesn’t pick up on variables and whatnot in your regular CSS. If you use sensible CSS class names scoping isn’t a big problem.

hello.css
1
2
3
4
.component-hello {
font-size: var(--someprojectvariable, 2em);
text-align: center;
}

There’s our same Vue component, with none of the problems the single-file Vue format brings with it. It isn’t all rainbows and sunshine - not having the CSS bundled in the same file as the HTML and JS is less elegant. But the payoff is pretty big.

I don’t know what the right answer is, but it’s something to consider for your next Vue project.