使用webpack-dev-middleware正确配置vuejs项目

7

我是一个新手,所以可能我犯了一个愚蠢的错误。这是我的项目设置(根目录下至少是相关部分):

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

现在我想使用webpack-dev(和hot)-middleware来提供我的index.html,并从我的src文件夹中创建一个内存中的bundle。现在我可以设置中间件(通过npm页面),并且我可以看到bundle被创建(通过控制台日志),但有两件事对我来说不清楚:
  • 如何提供index.html
  • 如何使用内存中创建的bundle?
请问有人能够解释一下这个中间件的工作原理吗?这是我的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
    })
  ])
}
1个回答

10

如何提供index.html文件

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

package.json

"scripts": {
  "dev": "node dev-server.js",
  ...
在webpack中,这个开发服务器通常会使用express,并且你需要将配置传递给你的express服务器来提供index.html。如果你想要热重载,那么你需要通过中间件层将webpack配置传递给express。
要进行热重载,你需要两个主要的中间件:
- 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中引用该文件的任何引用所指向的地方,即<script src="/dist/build.js"></script>

您可以手动完成此操作,但我们通常会添加更多的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。 - Simon Temmerman
没问题,如果有帮助,请标记为已回答。我马上看一下你的server.js文件,看是否有任何值得注意的地方。 - GuyC
看了一下配置文件,我从来没有使用过集群,所以不确定是否会导致问题。如果您在 app.listen(... 上面添加最终的 dev-server 配置,而不是使用 ./bin/www 的配置,它是否能正常工作? - GuyC
当然,我已经标记为已回答 :) 不幸的是,它并没有解决问题 :( 再次出现404。还有更多的想法吗?我将再次检查中间件的文档。 - Simon Temmerman
啊哈!我解决了!其实是一个有点傻的错误 :) 我把插件放在了一个 if 块里,只有当环境设置为生产时才运行。所以我把插件放在了 if 块外面的配置中,html 插件正确运行,现在当我访问 http://localhost:3000/dist/index.html 时可以得到索引页面并进行热重载。太棒了!我会更新我的问题,并附上我现在的 webpack 配置。 - Simon Temmerman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接