【问题标题】:Correctly configure webpack-dev-middleware with vuejs project用 vuejs 项目正确配置 webpack-dev-middleware
【发布时间】:2017-04-11 21:15:22
【问题描述】:

我是 webpack 的新手,所以这可能是我的一个愚蠢的错误。

这是我的项目设置(root,至少相关部分):

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

现在我想使用 webpack-dev(和 hot)-middleware 来服务我的 index.html 并从我的 src 文件夹在内存中创建一个包。现在我可以设置中间件(通过 npm 页面),我看到包已创建(通过登录控制台),但有两件事我不清楚:

  • 如何提供 index.html
  • 如何使用在内存中创建的包?

有人能解释一下这个中间件是如何工作的吗?这是我的 webpack 配置文件(需要插入中间件,它只是通过 vue-cli 创建的 webpack 配置文件的副本):

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

我知道这可能不是正确的配置,有人可以指出一些事情吗?

提前感谢您的帮助!

编辑(此设置有效)

我的 server.js 现在:

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};

我的 ./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);
}

我的工作 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
    })
  ])
}

【问题讨论】:

    标签: express webpack vue.js


    【解决方案1】:

    如何提供 index.html

    要提供 index.html 文件,您需要运行一个开发服务器。听起来您正在根据您在内存中创建捆绑包的记录成功地做到了这一点。但是我在您的设置中看不到它的文件?我假设下面的另一个文件名为:dev-server.js,它将是您为应用程序提供服务的入口点,即npm run dev

    package.json

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

    在 webpack 中,这个开发服务器通常是 express 的,它是你传递给 express 服务器的配置,它将为你的 index.html 提供服务。当您想要热重载时,您将通过中间件层传递您的 webpack 配置。

    对于热重载,您需要两个主要的中间件:

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

    然后你需要将你的配置传递给 webpack 并将你的 webpack 编译器传递给中间件,即

    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)
    

    现在密钥文件变成了你的 webpack 配置,上面引用为:./webpack.config.js

    这就引出了你的下一个问题:如何使用在内存中创建的包?

    您在上面发布的文件看起来是正确的,关于使用捆绑包的关键部分保存在输出中,您有:

    webpack.config.js

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

    您正在相对于当前工作目录的dist/build.js 创建捆绑包。这基本上是您需要在 index.html 中指向该文件的任何引用的地方,即&lt;script src="/dist/build.js"&gt;&lt;/script&gt;

    您可以手动执行此操作,但我们通常会添加一个进一步的 webpack 插件以在您的输出 html 中自动构建此脚本标签(在本例中也是在内存中):

    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
      }),
      ...
    

    HtmlWebpackPlugin 现在基本上是您引用输出文件的方式:filename 以及至关重要的存储位置:template 因此,如果您想移动 index.html 文件,这就是您告诉 webpack 到哪里找到它。 HtmlWebpackPlugin 现在会将输出“index.html”放在前面提到的publicPath,因此要访问此文件,您可以调用/dist/index.html

    最后,您需要一些其他插件来进行热重载,因此您的整个 webpack 插件数组将如下所示:

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

    现在我们返回到 dev-server.js 文件——或者你在其中配置 express 的任何文件——并启动配置的 express 服务器:

    dev-server.js

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

    因此,使用上述配置,您将打开以下 uri:localhost:8080/dist/index.html

    【讨论】:

    • 这个答案+1,只是因为我的分数太低而没有出现。谢谢,真的帮助我理解了!我现在在 localhost:3000/dist/index.html 得到一个 404。这可能是因为我正在使用集群吗?我用最新的 server.js、webpack 配置和 ./bin/www 更新了我的问题。
    • 别担心,如果有帮助,您可以标记为已回答。我会在几秒钟内看看你的 server.js - 看看有没有什么突出的
    • 查看我从未使用过集群的配置,所以我不确定这是否会导致问题。如果您添加上面的最终开发服务器配置:app.listen(... 并且不使用您的 `./bin/www` 配置,它是否有效?
    • 当然,我将其标记为已回答 :) 不,遗憾的是它并没有解决问题 :( 再次出现 404。还有什么想法吗?我将再次检查中间件的文档。
    • 啊哈!我解决了!真是个愚蠢的错误:) 我将插件放在一个 if 块中,该块仅在 env 设置为生产时运行。因此,我将插件放在 if 块之外的配置中,html 插件运行正确,现在当我通过热重载访问 localhost:3000/dist/index.html 时,我得到了索引页面。惊人的!我将使用我现在拥有的 webpack 配置更新我的问题。
    猜你喜欢
    • 2016-05-25
    • 2017-07-06
    • 2016-12-13
    • 2017-12-20
    • 2017-11-28
    • 2018-08-25
    • 2018-11-26
    • 2018-05-21
    • 2019-03-02
    相关资源
    最近更新 更多