# Single File Components
# Introduction
In many Vue projects, global components will be defined using app.component()
, followed by app.mount('#app')
to target a container element in the body of every page.
This can work very well for small to medium-sized projects, where JavaScript is only used to enhance certain views. In more complex projects however, or when your frontend is entirely driven by JavaScript, these disadvantages become apparent:
- Global definitions force unique names for every component
- String templates lack syntax highlighting and require ugly slashes for multiline HTML
- No CSS support means that while HTML and JavaScript are modularized into components, CSS is conspicuously left out
- No build step restricts us to HTML and ES5 JavaScript, rather than preprocessors like Pug (formerly Jade) and Babel
All of these are solved by single-file components with a .vue
extension, made possible with build tools such as Webpack or Browserify.
Here's an example of a file we'll call Hello.vue
:
Now we get:
- Complete syntax highlighting (opens new window)
- CommonJS modules (opens new window)
- Component-scoped CSS (opens new window)
As promised, we can also use preprocessors such as Pug, Babel (with ES2015 modules), and Stylus for cleaner and more feature-rich components.
These specific languages are only examples. You could as easily use TypeScript, SCSS, PostCSS, or whatever other preprocessors that help you be productive. If using Webpack with vue-loader
, it also has first-class support for CSS Modules.
# What About Separation of Concerns?
One important thing to note is that separation of concerns is not equal to separation of file types. In modern UI development, we have found that instead of dividing the codebase into three huge layers that interweave with one another, it makes much more sense to divide them into loosely-coupled components and compose them. Inside a component, its template, logic and styles are inherently coupled, and collocating them actually makes the component more cohesive and maintainable.
Even if you don't like the idea of Single-File Components, you can still leverage its hot-reloading and pre-compilation features by separating your JavaScript and CSS into separate files:
<!-- my-component.vue -->
<template>
<div>This will be pre-compiled</div>
</template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>
2
3
4
5
6
# Getting Started
# Example Sandbox
If you want to dive right in and start playing with single-file components, check out this simple todo app (opens new window) on CodeSandbox.
# For Users New to Module Build Systems in JavaScript
With .vue
components, we're entering the realm of advanced JavaScript applications. That means learning to use a few additional tools if you haven't already:
Node Package Manager (npm): Read the Getting Started guide (opens new window) section about how to get packages from the registry.
Modern JavaScript with ES2015/16: Read through Babel's Learn ES2015 guide (opens new window). You don't have to memorize every feature right now, but keep this page as a reference you can come back to.
After you've taken a day to dive into these resources, we recommend checking out Vue CLI (opens new window). Follow the instructions and you should have a Vue project with .vue
components, ES2015, webpack and hot-reloading in no time!
# For Advanced Users
The CLI takes care of most of the tooling configurations for you, but also allows fine-grained customization through its own config options (opens new window).
In case you prefer setting up your own build setup from scratch, you will need to manually configure webpack with vue-loader (opens new window). To learn more about webpack itself, check out their official docs (opens new window) and webpack learning academy (opens new window).
# Building with rollup
Most of the time when developing a third-party library we want to build it in a way that allows the consumers of the library to tree shake (opens new window) it. To enable tree-shaking we need to build esm
modules. Since webpack and, in turn, vue-cli do not support building esm
modules we need to rely on rollup (opens new window).
# Installing Rollup
We will need to install Rollup and a few dependencies:
npm install --save-dev rollup @rollup/plugin-commonjs rollup-plugin-vue
These are the minimal amount of rollup plugins that we need to use to compile the code in an esm
module. We may want to also add rollup-plugin-babel (opens new window) to transpile their code and node-resolve (opens new window) if we use dependencies that we want to bundle with our library.
# Configuring Rollup
To configure our build with Rollup we will need to create a rollup.config.js
file in the root of our project:
touch rollup.config.js
Once the file is created we will need to open it with our editor of choice and add the following code.
// import our third party plugins
import commonjs from 'rollup-plugin-commonjs'
import VuePlugin from 'rollup-plugin-vue'
import pkg from './package.json' // import our package.json file to re-use the naming
export default {
// this is the file containing all our exported components/functions
input: 'src/index.js',
// this is an array of outputted formats
output: [
{
file: pkg.module, // the name of our esm library
format: 'esm', // the format of choice
sourcemap: true, // ask rollup to include sourcemaps
}
],
// this is an array of the plugins that we are including
plugins: [
commonjs(),
VuePlugin()
],
// ask rollup to not bundle Vue in the library
external: ['vue']
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Configuring package.json
To take advantage of our newly created esm
module we need to add a few fields in our package.json
file:
"scripts": {
...
"build": "rollup -c rollup.config.js",
...
},
"module": "dist/my-library-name.esm.js",
"files": [
"dist/",
],
2
3
4
5
6
7
8
9
Here we are specifying:
- how to build our package
- what files we want to bundle in our package
- what file represents our
esm
module
# Bundling umd
and cjs
modules
To also build umd
and cjs
modules we can simply add a few lines of configuration to our rollup.config.js
and package.json
# rollup.config.js
output: [
...
{
file: pkg.main,
format: 'cjs',
sourcemap: true,
},
{
file: pkg.unpkg,
format: 'umd',
name: 'MyLibraryName',
sourcemap: true,
globals: {
vue: 'Vue',
},
},
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# package.json
"module": "dist/my-library-name.esm.js",
"main": "dist/my-library-name.cjs.js",
"unpkg": "dist/my-library-name.global.js",
2
3