热模块重新加载是使初始页面请求10-20s,与Webpack,Koa,Vue.js

出于某种原因,大多数页面刷新重新请求bundle.js文件,它需要大约10-15-20秒从本地主机下载。 这一切都来自localhost,bundle.js文件大小约为1mb。 这个文件的请求似乎只是爬行,一次加载几千字节。

一些观察:

  • 经过一番挖掘,似乎在从__webpack_hmr到服务器的初始调用中停顿,但是我不确定在调用bundle.js之后发生了这个调用。 以下是服务器请求stream的日志。

  • 在只有一个或两个组件的页面上, 除了主页以外的任何东西。 这暗示着它可能与热模块重装有关。

  • 与其他页面一样,首页仍然会花费5秒以上(有时10-20),但如果使用Ctrl + R刷新页面,则会立即返回。 如果我进行地址栏刷新,则需要更长的时间。 其他页面仍然需要一样长的时间,无论我按Ctrl + R或做一个地址栏重新加载…
  • 更新 :我删除了热门的模块replace,而且它似乎是问题的来源,因为没有它的网页加载即时。

请求日志:

– 响应时间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

然后在chrome控制台中,对于这个请求显示: 在这里输入图像描述

这是我的设置:

  • 使用Koa作为服务器环境(在初始响应中使用stream式传输/分块)
  • 使用webpack与热模块重新加载
  • 使用Vue.js作为前端框架和服务器端渲染
  • bundle.js通过典型的serve-static包提供
  • bundle.js似乎并没有被caching。 为什么是这样?

在Koa方面,我开始使用一些样板包来做所有这些服务器端渲染。 这一直发生,因为我开始搞乱这个设置和一般的webpack,所以我试图find它的底部。 这似乎有点随意,有时会在<1秒内回来,但大部分时间需要10秒以上。 有时候30+秒?!

我也试图使用不同的库来提供静态文件,但他们似乎都这样做。

这是我的主要webpackconfiguration('webpack.client',扩展如下):

'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 ] } 

而且这个(扩展以前):

 '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 

这是我的服务器Koa的index.js文件:

 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}`) }) 

任何人都知道为什么HMR的初始请求会花费这么长时间,似乎是如此随机(有时5秒,有时30秒)? Techneregy。