6

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

一些观察:

  • 经过一番挖掘后,从 __webpack_hmr 对服务器的初始调用似乎停滞不前,但我不确定这个调用是在调用 bundle.js 之后发生的。下面是服务器请求流的日志。

  • 它仅在具有超过一两个组件的页面上很慢,即。主页以外的任何内容。这暗示了它可能与热模块重新加载有关的想法。

  • 与其他页面一样,主页仍将花费 > 5 秒(有时 10-20 秒),但如果我使用 Ctrl+R 刷新页面,它几乎会立即返回。如果我进行地址栏刷新,则需要更长的时间。无论我是 Ctrl+R 还是重新加载地址栏,其他页面仍然需要同样长的时间......
  • 更新:我删除了热模块替换,这似乎是问题的根源,因为没有它,页面会立即加载。

请求日志:

-- 响应时间 GET / = 609ms
--> GET / 200 647ms 2.55kb
<-- GET /main.aafc9fb7f6a0c7f127edb04734d29547.css --
> GET /main.aafc9fb7f6a0c7f127edb04734d29547.css 200 17ms.jskb -4
<bundle 3.4
> GET /bundle.js 200 18ms 1.29mb
<-- GET /__webpack_hmr

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

这是我的设置:

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

在 Koa 方面,我从一些样板包开始做所有这些服务器端渲染等。自从我开始搞乱这个设置和一般的 webpack 以来,这种情况一直在发生,所以我试图深入了解它。它似乎有点随机,有时它会在 < 1 秒内返回,但大多数时候需要 10 多秒。有时30多秒?!

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

这是我的主要 webpack 配置('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 秒)?技术。

4

0 回答 0