webpack-dev-server是一个封装好的webpack开发服务器,底层使用express。通常用在开发环境的webpack打包,它有以下这些作用:

  1. 读取webpack.config.js并使用webpack进行编译
  2. 默认集成一些第三方插件并可供配置,都在webpack.config.js下的devServer节点下(本节重点)
  3. 开启一个websocket以实现热更新
  4. 开启本地express服务器以实现网址预览

webpack打包和webpack-dev-server开启服务的区别:webpack输出真实的文件,而webpack-dev-server输出的文件只存在于内存中,不输出真实的文件

devServer配置

webpack的devServer配置基于webpack-dev-server(opens new window)集成的插件。该插件提供了proxy代理配置,基于express中间件 http-proxy-middleware(opens new window)实现,该中间件又基于node http-proxy(opens new window),所以如果要详细知道proxy各个参数的意义和实现方式,可以阅读下http-proxy的源码。
proxy作用:解决开发环境的跨域问题(不用再去配置nginx)

  1. devServer: {
  2. // 提供静态文件目录地址
  3. // 基于express.static实现
  4. contentBase: path.join(__dirname, 'dist'),
  5. // 任意的 404 响应都被替代为 index.html
  6. // 基于node connect-history-api-fallback包实现
  7. historyApiFallback: true,
  8. // 是否一切服务都启用 gzip 压缩
  9. // 基于node compression包实现
  10. compress: true,
  11. // 是否隐藏bundle信息
  12. noInfo: true,
  13. // 发生错误是否覆盖在页面上
  14. overlay: true,
  15. // 是否开启热加载
  16. // 必须搭配webpack.HotModuleReplacementPlugin 才能完全启用 HMR
  17. // 如果 webpack webpack-dev-server 是通过 --hot 选项启动的,那么这个插件会被自动添加
  18. hot: true,
  19. // 热加载模式
  20. // true代表inline模式,false代表iframe模式
  21. inline: true, // 默认是true
  22. // 是否自动打开
  23. open: true,
  24. // 设置本地url和端口号
  25. host: 'localhost',
  26. port: 8080,
  27. // 代理
  28. // 基于node http-proxy-middleware包实现
  29. proxy: {
  30. // 匹配api前缀时,则代理到3001端口
  31. // http://localhost:8080/api/123 = http://localhost:3001/api/123
  32. // 注意:这里是把当前server8080代理到3001,而不是任意端口的api代理到3001
  33. '/api': 'http://localhost:3001',
  34. // 设置为true, 本地就会虚拟一个服务器接收你的请求并代你发送该请求
  35. // 主要解决跨域问题
  36. changeOrigin: true,
  37. // 针对代理https
  38. secure: false,
  39. // 覆写路径:http://localhost:8080/api/123 = http://localhost:3001/123
  40. pathRewrite: {'^/api' : ''}
  41. }
  42. }

源码解析

webpack-dev-server源码,把一些细枝末节去掉应该好理解很多,大部分都是基于第三方插件封装的API:

  1. const features = {
  2. // compress
  3. // 基于compression
  4. compress: () => {
  5. if (options.compress) {
  6. // Enable gzip compression.
  7. app.use(compress());
  8. }
  9. },
  10. // proxy
  11. // 基于http-proxy-middleware
  12. proxy: () => {
  13. const getProxyMiddleware = (proxyConfig) => {
  14. const context = proxyConfig.context || proxyConfig.path;
  15. if (proxyConfig.target) {
  16. return httpProxyMiddleware(context, proxyConfig);
  17. }
  18. };
  19. // 多个proxy设置
  20. options.proxy.forEach((proxyConfigOrCallback) => {
  21. // http-proxy-middleware作为中间件应用到express
  22. // 如果自己封装代理,这里很值得借鉴
  23. app.use((req, res, next) => {
  24. const proxyConfig = proxyConfigOrCallback();
  25. proxyMiddleware = getProxyMiddleware(proxyConfig);
  26. proxyMiddleware(req, res, next);
  27. })
  28. }
  29. },
  30. // historyApiFallback
  31. // 基于connect-history-api-fallback
  32. historyApiFallback: () => {
  33. if (options.historyApiFallback) {
  34. // Fall back to /index.html if nothing else matches.
  35. app.use(historyApiFallback(fallback));
  36. }
  37. },
  38. // contentBase
  39. // 基于express.static
  40. contentBaseFiles: () => {
  41. if (Array.isArray(contentBase)) {
  42. contentBase.forEach((item) => {
  43. app.get('*', express.static(item));
  44. });
  45. }
  46. }
  47. }