Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correctly configure webpack-dev-middleware with vuejs project

I'm new to webpack so it's probably a stupid mistake on my part.

This is my project setup (root, atleast the relevant part):

 +-- public
 |  |  
 |  +-- index.html
 |    
 +-- src
 |  |  
 |  +-- App.vue
 |  +-- main.js
 |  +-- assets
 |    
 +-- package.json
 |    
 +-- webpack.config.js

Now I would like to use the webpack-dev(and hot)-middleware to serve my index.html and create a bundle in memory from my src folder. Now I can get the middleware set up (via the npm page) and I see the bundle gets created (through logging in console) but two things are not clear to me:

  • How to serve index.html
  • How to use the bundle thats created in memory?

Can somebody please explain how that middleware works exactly? This is my webpack config file (which is needed to plug into the middleware, it's just a copy of the webpack config file that gets created through the vue-cli):

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

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

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  // http://vue-loader.vuejs.org/en/workflow/production.html
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}

I know this is probably not the correct configuration, can someone maybe point out some things?

Thanks in advance for your help!

Edit (this setup works):

My server.js now:

var express = require('express');
var logger = require('morgan');
var bodyParser = require('body-parser');
var exphbs  = require('express-handlebars');
var helmet = require('helmet');
var redis = require('redis');
var redisAdapter = require('socket.io-redis');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
// use the redis adapter to create sticky sessions, this is needed because of the different clusters
io.adapter(redisAdapter( require('./app/lib/config').credentials.redis.url ));

//setup security ===============================================================
require('./app/lib/security-setup')(app, helmet);

// configuration ===============================================================
app.use(logger('dev')); // log every request to the console

// set up our express application ==============================================

// Make the body object available on the request
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//set handlebars as default templating engine
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');

// serve the static content ====================================================
if (app.settings.env === 'development') {
  var webpackConfig = require('./webpack.config.js')
  var compiler = require('webpack')(webpackConfig)
  var devMiddleware = require('webpack-dev-middleware')(compiler, {
    publicPath: webpackConfig.output.publicPath,
  })
  var hotMiddleware = require('webpack-hot-middleware')(compiler)

  app.use(devMiddleware)
  app.use(hotMiddleware)
} else {
    app.use(express.static(__dirname + '/public'));
}

// set up global variables =====================================================
app.use(function(req, res, next) {
  //set the io object on the response, so we can access it in our routes
  res.io = io;
  next();
});

// routes ======================================================================
require('./app/routes.js')(app); // load our routes and pass in our app

// export so bin/www can launch ================================================
module.exports = {app: app, server: server};

my ./bin/www:

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../server').app;
var cluster = require('cluster');
var debug = require('debug')('temp:server');
var http = require('http');
var numCPUs = process.env.WORKERS || require('os').cpus().length;

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // If a worker dies, log it to the console and start another worker.
  cluster.on('exit', function(worker, code, signal) {
    console.log('Worker ' + worker.process.pid + ' died.');
    cluster.fork();
  });

  // Log when a worker starts listening
  cluster.on('listening', function(worker, address) {
    console.log('Worker started with PID ' + worker.process.pid + '.');
  });

} else {
  /**
   * Create HTTP server.
   */

  var server = require('../server').server;

  /**
   * Listen on provided port, on all network interfaces.
   */
  server.listen(port);

  server.on('error', onError);
  server.on('listening', onListening);
}

// The rest of the bin/www file.....

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

My working webpack.config.js:

var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: [
    'webpack/hot/dev-server',
    'webpack-hot-middleware/client',
    './src/main.js'
  ],
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'build.js'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          // vue-loader options go here
        }
      },
      {
        test: /\.less$/,
        loader: "style!css!less"
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]?[hash]'
        }
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.common.js'
    }
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true
  },
  devtool: '#eval-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: path.resolve(__dirname, 'public/index.html'),
      inject: true
    }),
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ]
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  // http://vue-loader.vuejs.org/en/workflow/production.html
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}
like image 202
Simon Temmerman Avatar asked Nov 28 '16 07:11

Simon Temmerman


1 Answers

How to serve index.html

To serve the index.html file you need to have a dev server running. It sounds like you are successfully doing this based on your logging of the creation of your bundle in memory. However I can't see a file for it in your set up? I've assumed another file below called: dev-server.js and it would be your entry point to serving your app, i.e. npm run dev:

package.json

"scripts": {
  "dev": "node dev-server.js",
  ...

In webpack this dev server is generally going to be express and it's the config that you pass to your express server that will serve your index.html. As you are wanting hot-reloading you will pass express your webpack config through your middleware layers.

For hot reloading you'll need two main middlewares:

  • webpack-dev-middleware
  • webpack-hot-middleware

Then you will need to pass your config to webpack and pass your webpack compiler to the middleware, i.e.

dev-server.js

var app = require('express')()

var webpackConfig = require('./webpack.config.js')

var compiler = require('webpack')(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
})

var hotMiddleware = require('webpack-hot-middleware')(compiler)

app.use(devMiddleware)

app.use(hotMiddleware)

Now the key file becomes your webpack config, referenced above as: ./webpack.config.js

This leads to your next question: How to use the bundle thats created in memory?

The file you posted above looks to be about right and the key parts in regards to using the bundle are held within the output, you have:

webpack.config.js

output: {
  path: path.resolve(__dirname, './dist'),
  publicPath: '/dist/',
  filename: 'build.js'
},

You are creating the bundle at dist/build.js relative to the current working directory. This is essentially where you need to point any references to that file within your index.html, i.e. <script src="/dist/build.js"></script>

How you do this can be manual however we'd often add a further webpack plugin to automatically build this script tag within your output html (again in this case in memory):

webpack.config.js

plugins: [
  // https://github.com/ampedandwired/html-webpack-plugin
  new HtmlWebpackPlugin({
    filename: 'index.html',
    template: path.resolve(__dirname, 'public/index.html'),
    inject: true
  }),
  ...

The HtmlWebpackPlugin is now essentially how you reference what the output file is called: filename and crucially where it is stored: template so if you wanted to move your index.html file this is where you tell webpack where to find it. The HtmlWebpackPlugin will now place the output 'index.html' at the publicPath referenced earlier, so to get to this file you would call /dist/index.html.

Finally you'll need some further plugins for hot reloading so your entire webpack plugins array will look like:

webpack.config.js

plugins: [
  new webpack.optimize.OccurenceOrderPlugin(),
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoErrorsPlugin(),
  // https://github.com/ampedandwired/html-webpack-plugin
  new HtmlWebpackPlugin({
    filename: 'index.html',
    template: 'bar/index.html',
    inject: true
  })
]

Now we return to the dev-server.js file - or whatever yours is called that you are configuring express within - and fire up the configured express server:

dev-server.js

module.exports = app.listen(8080, function (err) {
  if (err) {
    console.log(err)
    return
  }
})

so with the above config you'd open the following uri: localhost:8080/dist/index.html

like image 59
GuyC Avatar answered Nov 05 '22 22:11

GuyC