From npm init to a Single-Page Application in Vue.js, Part 1

This blog post will detail the necessary setup for a Vue.js single page application. It will also detail a webpack setup to minify and transpile our .vue files into a single, minified javascript file that we can “ship” to the browser. To get started we’ll head to the command line:

mkdir MyApp; cd ./MyApp; npm init

Fill in the fields as needed for your package file. After we’re done we’ll install Vue and the Vue router. I’m also going to install the VueX state manager so that we can save application state if necessary. So to install these as production dependencies we need to run:

npm install --save vue vue-router vuex

Now we need to setup our webpack dev server and our transpiling engine so that we can use ES6 features for development. We’ve got a pretty long list of dependencies to add to get this setup. We’ll start with babel so we can use ES6 features to set up our webpack file.

npm install --save-dev babel-core babel-loader babel-preset-env

This will install:

  1. babel-core – The core transpiler to transform ES6 code to browser compliant code.
  2. babel-loader – This is a loader that takes our transformed code and feeds it to webpack.
  3. babel-preset-env – This sets up some common environment variables so you don’t have to spend a lot of time setting up your babel configuration.

We now need a .babelrc file so that babel knows how to transpile our code. The basics setup by babel-preset-env should work for us, but for to work well with babel and webpack we need to turn off namespacing. babel-preset-env defaults to the commonjs module spec, but we don’t want to use a specific module spec since Vue handles that for us with the vue-loader. Our final .babelrc file should look like this:

  "presets": [
    ["env", { "modules": false }]

Alright, babel’s in pretty good shape right now. So we can go ahead and move onto webpack.

npm install --save-dev webpack webpack-dev-server css-loader file-loader vue-loader vue-template-compiler cross-env

I installed cross-env because I’m on a windows system; Windows is not a POSIX-compliant environment so if we tried to set the environment for node we would get an error. cross-env will handle setting environment variables for us, but if you’re using a POSIX-compliant system you can just skip installing cross-env. Now that we’ve got webpack installed we need to start building out our config file. Create a webpack.config.js file in the root of your directory.

At the top we require the path object from the node.js standard API and we require webpack to interpret our configuration. The module created after that will be fed to webpack as a configuration file.

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'build.js'
    module: {
        rules: [
                test: /\.vue$/,
                loader: 'vue-loader'                
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
                test: /\.(png|jpg|gif)$/,
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]?[hash]'
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
    devServer: {
        historyApiFallback: true,
        noInfo: true
    performance: {
        hints: "warning"
    devtool: "#eval-source-map"

There’s a lot to this file but I’m going to try to break it down by each property in the module.

  • Entry – The entry point for our application, our root Vue instance will be created here.
  • Output – An object that describes how we want our packed application to be delivered. Most properties are pretty self-explanatory.
    • path – The path to the final packed file. This is where node’s path utility comes in handy.
    • publicPath – The publicly accessible path to the final packed file.
    • filename – The final filename of the final packed file.
  • Module – This is an object that helps us define how we break out our code.
    • rules – An array of objects that pack our files.
      • Our vue loader object, it can also take an options object to define different style template compilers if you want to use scss or sass.
      • Our js loader object, this sends all of our js files through babel so we can use ES6 features in our code.
      • Our file loader object, this will pack all of our images for use in the browser.
  • Resolve – This object changes how webpack finds our modules. Our alias key will help webpack find Vue.js and include it in our final package.
    • Alias – Regular expression matching on vue is the key while the path to the vue ES module
  • devServer – This key defines how our development server behaves.
    • historyApiFallback – If true all http error codes return the index page, can also take an array of code-page resolutions for different error codes.
    • noInfo – This just keeps our command line quiet while the server is running.
  • performance – Defines how our webpack transpiler will display performance hints.
    • hints – Takes an enum, if false no performance hints will be shown, if warning it will show a warning, if error it will error out. Based on the size of the final package.
  • devtool – Defines which kind of source map we want to ship with our package. We’re using an eval source map because it is the closest to the original source before transpiling.

The good thing about webpack though is you can easily modify the configuration object based on the environment that you’re in. Making it easy to set up a development and build workflow.

if (process.env.NODE_ENV === 'production') {
    module.exports.devtool = "#source-map";
    module.exports.performance.hints = "error";
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env' :{
                NODE_ENV: '"procuction"'
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            compress: {
                warnings: false
        new webpack.LoaderOptionsPlugin({
            minimize: true

This block defines a constant across our webpack configuration for a production environment, minifies our code using uglify.js, and minifies everything loaded in from our loaders. If you follow the documentation for webpack you’ll see that you can also spread your configuration across multiple files sharing base configuration in a separate file from dev, test, or production. Now we need to setup our entry point where we can register our routes and components and create a root Vue instance. Make a folder called src and create an index.js file inside of it.

import Vue from 'vue'
import App from './App.vue'

new Vue({
    el: "#app",
    render: h => h(App)

This sets up our root Vue instance and renders the App module to the browser. This module is imported from a .vue file that you’ll create in the same src directory:

<div id="app">
    <h1>{{ message }}</h1>    
export default {
    name: 'app',
    data () {
        return {
            message: "Welcome to vue.js!"

A Vue file contains a template for the HTML you expect in your component, a script for all the code necessary to run your component and a style collection for any CSS styles you expect for your component. Right now this doesn’t do anything particularly useful, it just shows a “Welcome to vue.js!” message as a header. We’ll clean it up in the next part and add some more functionality to it, for now let’s put our index page up and run our server.

<!DOCTYPE html>
<html lang="en">
        <meta charset="utf-8">
        <title>Webpacking it!</title>
        <link rel="stylesheet" href="">
        <div id="app"></div>
        <script src=""></script>        

The ‘el’ property in our root Vue instance corresponds to the root div id in the index document for our development server. We also import our main style sheet (I like mini.css) and our main JavaScript file. Now if we run our development server with the following command (Note: You may need to install the webpack-dev-server cli tools by running npm install -g webpack-dev-server ):


After this you can navigate to localhost:8080 and see your webpage:

Unfortunately, just running webpack-dev-server won’t hot reload your files. Let’s add a couple of NPM scripts. In the scripts section of your project.json file add the following code:

"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",

So now, when you invoke npm run dev , the environment will be setup as development, the webpack-dev-server will start with hot reloading, and your default browser will open to localhost:8080. We can also use npm run build  to build our application so that we can ship it to any browser. In the next blog post, we’ll set up a vue-router to handle requests to our server and build a client-side crud application with an admin panel.