Vue.js 2.x Development Build With Hot Reloading For External Server (using Webpack template)

Sep 12, 2017

NOTE: Refer to this for latest guide.

This article assuming you created your project using webpack template.

vue init webpack <PROJECT_NAME>

Open package.json and observe the scripts section. You realize there is dev to spin up a development server with hot reload and build for production release. There isn't an option for development release with hot reloading (watch mode).

Note: if you are not using webpack, you can use vue build index.js --watch to enable watch mode.

...
  "scripts": {
    "dev": "node build/dev-server.js",
    "start": "node build/dev-server.js",
    "build": "node build/build.js",
    "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
    "e2e": "node test/e2e/runner.js",
    "test": "npm run unit && npm run e2e",
    "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
  },
...

Edit package.json to add watch.

```text
...
  "scripts": {
    ...
    "watch": "node build/watch-build-dev.js"
  },
...

Copy build/build.js to build/watch-build-dev.js, and edit as per following (changes are commented with EDIT).

require('./check-versions')()// EDIT: process.env.NODE_ENV = 'production'process.env.NODE_ENV = 'development'var ora = require('ora')var rm = require('rimraf')var path = require('path')var chalk = require('chalk')var webpack = require('webpack')var config = require('../config')// EDIT: var webpackConfig = require('./webpack.prod.conf')var webpackConfig = require('./webpack.watch.conf')// EDIT: ora('building for production...')var spinner = ora('building for development...')spinner.start()rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {  if (err) throw err  webpack(webpackConfig, function (err, stats) {    spinner.stop()    if (err) throw err    process.stdout.write(stats.toString({      colors: true,      modules: false,      children: false,      chunks: false,      chunkModules: false    }) + '\n\n')    // EDIT: remove the following to avoid watch quit upon error    /*    if (stats.hasErrors()) {      console.log(chalk.red('  Build failed with errors.\n'))      process.exit(1)    }     */    console.log(chalk.cyan('  Build complete.\n'))    // EDIT: remove    /*    console.log(chalk.yellow(      '  Tip: built files are meant to be served over an HTTP server.\n' +      '  Opening index.html over file:// won\'t work.\n'    ))     */  })})

Copy build/webpack.dev.conf.js to build/webpack.watch.conf.js, and edit as per following (changes are commented with EDIT).

Update 2017-11-26: for vue-cli-template-webpack 1.2.4, copy build/webpack.prod.conf.js to build/webpack.watch.conf.js, and edit as per following (changes are commented with EDIT)

'use strict'const path = require('path')const utils = require('./utils')const webpack = require('webpack')const config = require('../config')const merge = require('webpack-merge')const baseWebpackConfig = require('./webpack.base.conf')const CopyWebpackPlugin = require('copy-webpack-plugin')const HtmlWebpackPlugin = require('html-webpack-plugin')const ExtractTextPlugin = require('extract-text-webpack-plugin')const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')// EDIT: change to development// const env = require('../config/prod.env')const env = require('../config/dev.env')const webpackConfig = merge(baseWebpackConfig, {  module: {    rules: utils.styleLoaders({      sourceMap: config.build.productionSourceMap,      extract: true,      usePostCSS: true    })  },  devtool: config.build.productionSourceMap ? config.build.devtool : false,  output: {    path: config.build.assetsRoot,    // EDIT: remove hash    // filename: utils.assetsPath('js/[name].[chunkhash].js'),    // chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')    filename: utils.assetsPath('js/[name].js'),    chunkFilename: utils.assetsPath('js/[id].js')  },  plugins: [    // http://vuejs.github.io/vue-loader/en/workflow/production.html    new webpack.DefinePlugin({      'process.env': env    }),    // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify    // EDIT: disable js minify    /*    new webpack.optimize.UglifyJsPlugin({      compress: {        warnings: false      },      sourceMap: config.build.productionSourceMap,      parallel: true    }),     */    // extract css into its own file    new ExtractTextPlugin({      // EDIT: remove hash      // filename: utils.assetsPath('css/[name].[contenthash].css'),      filename: utils.assetsPath('css/[name].css'),      // set the following option to `true` if you want to extract CSS from      // codesplit chunks into this main css file as well.      // This will result in *all* of your app's CSS being loaded upfront.      // EDIT: if not then splitting vendor.css will cause javascript error      // allChunks: false,      allChunks: true,    }),    // Compress extracted CSS. We are using this plugin so that possible    // duplicated CSS from different components can be deduped.    // EDIT: disable CSS minify    /*    new OptimizeCSSPlugin({      cssProcessorOptions: config.build.productionSourceMap      ? { safe: true, map: { inline: false } }      : { safe: true }    }),     */    // generate dist index.html with correct asset hash for caching.    // you can customize output by editing /index.html    // see https://github.com/ampedandwired/html-webpack-plugin    new HtmlWebpackPlugin({      filename: config.build.index,      template: 'index.html',      inject: true,      // EDIT: disable minify      /*      minify: {        removeComments: true,        collapseWhitespace: true,        removeAttributeQuotes: true        // more options:        // https://github.com/kangax/html-minifier#options-quick-reference      },       */      minify: false,      // necessary to consistently work with multiple chunks via CommonsChunkPlugin      chunksSortMode: 'dependency'    }),    // keep module.id stable when vender modules does not change    new webpack.HashedModuleIdsPlugin(),    // enable scope hoisting    // EDIT:    // new webpack.optimize.ModuleConcatenationPlugin(),    // split vendor js into its own file    new webpack.optimize.CommonsChunkPlugin({      name: 'vendor',      minChunks: function (module) {        // any required modules inside node_modules are extracted to vendor        // EDIT        return (          module.resource &&          /\.(js|css|sass|scss|less)$/.test(module.resource) &&          module.resource.indexOf(            path.join(__dirname, '../node_modules')          ) === 0        )      }    }),    // extract webpack runtime and module manifest to its own file in order to    // prevent vendor hash from being updated whenever app bundle is updated    new webpack.optimize.CommonsChunkPlugin({      name: 'manifest',      minChunks: Infinity    }),    // This instance extracts shared chunks from code splitted chunks and bundles them    // in a separate chunk, similar to the vendor chunk    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk    new webpack.optimize.CommonsChunkPlugin({      name: 'app',      async: 'vendor-async',      children: true,      minChunks: 3    }),    // copy custom static assets    new CopyWebpackPlugin([      {        from: path.resolve(__dirname, '../static'),        to: config.build.assetsSubDirectory,        ignore: ['.*']      }    ])  ],  // EDIT: enable watch  watch: true,  // EDIT: enable watch for modules  // https://github.com/vuejs-templates/webpack/issues/378  watchOptions: {    aggregateTimeout: 300,    poll: 1000  }})if (config.build.productionGzip) {  const CompressionWebpackPlugin = require('compression-webpack-plugin')  webpackConfig.plugins.push(    new CompressionWebpackPlugin({      asset: '[path].gz[query]',      algorithm: 'gzip',      test: new RegExp(        '\\.(' +        config.build.productionGzipExtensions.join('|') +        ')$'      ),      threshold: 10240,      minRatio: 0.8    })  )}if (config.build.bundleAnalyzerReport) {  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin  webpackConfig.plugins.push(new BundleAnalyzerPlugin())}module.exports = webpackConfig

Run npm run watch to enable development build in watch mode.

Note: if you include global scope CSS, remember to import them at main.js.

npm run watch# output> [email protected] watch /code/vue/hello> node build/watch-build-dev.js⠙ building for development... DONE  Compiled successfully in 2923ms                                5:32:25 PMHash: fcfe8096ae777ea47bf7Version: webpack 2.7.0Time: 2923ms             Asset       Size  Chunks                    Chunk Names            app.js     780 kB       0  [emitted]  [big]  appstatic/css/app.css   93 bytes       0  [emitted]         app        index.html  845 bytes          [emitted]           Build complete.

Output shall be created at dist folder. You can use symbolic link (ln -s /code/vue/hello/dist/app.js) to link to these files from you external server (e.g. nginx) development directory. You can use something like livereload on the external server development directory to reload if changes are detected.

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.