Hot Module Reloading is making initial page request take 10-20s, with Webpack, Koa, Vue.js
Asked Answered
P

0

6

For some reason most page refreshes re-request the bundle.js file and it takes about 10-15-20 seconds to download from localhost. This all from localhost, and the bundle.js file is about 1mb in size. The request for this file only seems to crawl, loading a few kilobytes at a time.

Some observations:

  • After some digging it seems to be stalling out on the initial call to the server from __webpack_hmr, but I'm not sure as this call happens after the call to bundle.js. Below is the log of the server request flow.

  • It is only slow on pages that have more than one or two components, ie. anything other than the homepage. This alludes to the idea that it might be related to the hot module reloading.

  • The homepage will still take > 5s (sometimes 10-20) just like the other pages, but if I refresh the page with Ctrl+R, it comes back nearly instantly. If I do an address-bar refresh, it takes longer. The other pages still take just as long no matter if I Ctrl+R or do an address-bar reload...
  • Update: I removed the hot module replacement, and it definitely seems to be the source of the issue, as the pages load instantly without it.

Request log:

-- Response time GET / = 609ms
--> GET / 200 647ms 2.55kb
<-- GET /main.aafc9fb7f6a0c7f127edb04734d29547.css
--> GET /main.aafc9fb7f6a0c7f127edb04734d29547.css 200 17ms 3.43kb
<-- /bundle.js
--> GET /bundle.js 200 18ms 1.29mb
<-- GET /__webpack_hmr

And then in the chrome console, for this request it shows: enter image description here

Here's my setup:

  • Using Koa as the server environment (using streaming/chunking in initial response)
  • Using webpack with hot module reloading
  • Using Vue.js as a frontend framework, with server-side rendering
  • bundle.js is served through the typical serve-static package
  • bundle.js doesn't seem to be being cached at all. Why is this?

On the Koa side of things, I started with some boilerplate package to do all this server-side rendering and such. This has been happening since I started messing around with this setup, and webpack in general, so I'm trying to get to the bottom of it. It seems to be a little random, where sometimes it will come back in < 1s, but most times it takes 10+ seconds. Sometimes 30+ seconds?!

I've also tried to use different libraries to serve the static files, but they all seem to do this.

Here is my main webpack config ('webpack.client', extended below):

'use strict'
const path = require('path')
const webpack = require('webpack')
const AssetsPlugin = require('assets-webpack-plugin')
const assetsPluginInstance = new AssetsPlugin({path: path.join(process.cwd(), 'build')})
const postcss = [
  require('precss')()
  //require('autoprefixer')({browsers: ['last 2 versions']}),
]

module.exports = {
  entry: [
    './src/client-entry.js'
  ],
  output: {
    path: path.join(process.cwd(), 'build'),
    filename: 'bundle.js',
    publicPath: '/'
  },
  resolve: {
    extensions: ['', '.vue', '.js', '.json']
  },
  module: {
    loaders: [
      {
        test: /\.vue$/,
        loaders: ['vue']
      },
      {
        test: /\.js$/,
        loaders: ['babel'],
        exclude: [/node_modules/]
      },
      {
        test: /\.json$/,
        loaders: ['json'],
        exclude: [/node_modules/]
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'url?limit=10000&name=images/[hash].[ext]',
        include: path.src,
      },
      {
        test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/,
        loader: 'url-loader',
        include: path.src,
      }
    ]
  },
  node: { net: 'empty', dns: 'empty' },
  postcss,
  vue: {
    postcss,
    loaders: {}
  },
  plugins: [
    assetsPluginInstance
  ]
}

And also this (extends the previous):

'use strict'
const webpack = require('webpack')
const config = require('./webpack.client')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

config.entry.push('webpack-hot-middleware/client')
//config.devtool = 'inline-eval-cheap-source-map'
config.plugins = config.plugins.concat([
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoErrorsPlugin(),
  new webpack.DefinePlugin({
    '__DEV__': true,
    'process.env.NODE_ENV': JSON.stringify('development')
  }),
  new ExtractTextPlugin('[name].[contenthash].css')
])
config.vue.loaders = {
  postcss: ExtractTextPlugin.extract(
    'vue-style-loader',
    'css-loader?sourceMap'
  ),
  css: ExtractTextPlugin.extract(
    'vue-style-loader',
    'css-loader?sourceMap'
  )
}

module.exports = config

Here is my server index.js file for Koa:

import path from 'path'
import fs from 'fs'
import Koa from 'koa'
import convert from 'koa-convert'
//import serve from 'koa-static-server'
import serveStatic from 'koa-static'
import {PassThrough} from 'stream'
import {createBundleRenderer} from 'vue-server-renderer'
import serialize from 'serialize-javascript'
import MFS from 'memory-fs'
import assets from '../build/webpack-assets'
import cookie from 'koa-cookie'

let renderer
const createRenderer = fs => {
  const bundlePath = path.resolve(process.cwd(), 'build/server-bundle.js')
  return createBundleRenderer(fs.readFileSync(bundlePath, 'utf-8'))
}

const app = new Koa();

app.use(cookie());

if (process.env.NODE_ENV === 'development') {
  // DEVELOPMENT, with hot reload
  const webpack = require('webpack')
  const webpackConfig = require('../config/webpack.client.dev')
  const compiler = webpack(webpackConfig)
  const devMiddleware = require('koa-webpack-dev-middleware')
  const hotMiddleware = require('koa-webpack-hot-middleware')

  app.use(convert(devMiddleware(compiler, {
    publicPath: webpackConfig.output.publicPath,
    stats: {
      colors: true,
      modules: false,
      children: false,
      chunks: false,
      chunkModules: false
    }
  })))

  app.use(convert(hotMiddleware(compiler)))

  // server renderer
  const serverBundleConfig = require('../config/webpack.bundle')
  const serverBundleCompiler = webpack(serverBundleConfig)
  const mfs = new MFS()

  serverBundleCompiler.outputFileSystem = mfs
  serverBundleCompiler.watch({}, (err, stats) => {
    if (err) throw err
    stats = stats.toJson()
    stats.errors.forEach(err => console.error(err))
    stats.warnings.forEach(err => console.warn(err))
    renderer = createRenderer(mfs)
  })
} 
else {
  // PRODUCTION
  // use nginx to serve static files in real
  //app.use(convert(serve({rootDir: path.join(process.cwd(), 'build'), rootPath: '/static'})))
  app.use(serveStatic(path.join(process.cwd(), 'build')));
  renderer = createRenderer(fs)
}

app.use(ctx => {
  var start = new Date;
  ctx.type = 'text/html; charset=utf-8'
  const context = {url: ctx.url}
  const title = 'Tripora';
  const stream = new PassThrough()

  console.log("Checking if server-side cookie exists...");
  // See if request sent over an authentication token in their cookies
  if(ctx.cookie && ctx.cookie.token) {
    console.log("Found cookie token.");
    context.token = ctx.cookie.token;
  }

  stream.write(`<!DOCTYPE html><html style="min-height: 100%;"><head><meta charset="utf-8"/><title>${title}</title>${assets.main.css ? `<link rel="stylesheet" href="${assets.main.css}"/>` : ''}</head><body style="min-height: 100%;">`)

  const renderStream = renderer.renderToStream(context)
  let firstChunk = true

  renderStream.on('data', chunk => {
    // we tell the request to ignore files as an initial reuqest
    var isPage = ctx.url.split(".").length == 1;

    if (firstChunk && context.initialState && isPage) { 
      stream.write(`<script>window.__INITIAL_STATE__=${serialize(context.initialState, {isJSON: true})}</script>${chunk}`)
      firstChunk = false
    } else {
      stream.write(chunk)
    }
  })

  renderStream.on('end', () => {
    stream.write(`<script src="${assets.main.js}"></script></body></html>`)

    var ms = new Date - start;
    //ctx.set('X-Response-Time', ms + 'ms');
    console.log("-- Response time %s %s = %sms", ctx.method, ctx.originalUrl, ms);

    ctx.res.end()
  })

  renderStream.on('error', err => {
    console.log("ERROR", err.stack);
    throw new Error(`something bad happened when renderToStream: ${err}`)
  })

  ctx.status = 200
  ctx.body = stream
})

const port = process.env.NODE_PORT || 80
app.listen(port, () => {
  console.log(`==> Listening at http://localhost:${port}`)
})

Anyone know why the HMR initial request would take so long, and seem to be so random (sometimes 5s, sometimes 30 seconds)? Techneregy.

Pichardo answered 11/10, 2016 at 6:11 Comment(1)
Did you ever figure this out?Weatherly

© 2022 - 2024 — McMap. All rights reserved.