tools.devServer
通过 tools.devServer 可以修改开发环境服务器的配置。
选项
after
添加自定义中间件,在所有开发环境中间件后执行。
export default {
  tools: {
    devServer: {
      after: [
        async (req, res, next) => {
          console.log('after dev middleware');
          next();
        },
      ],
    },
  },
};
 
webpack-dev-server 使用 Express 作为服务端框架。Modern.js 中没有使用任何框架,上述中间件中 req 和 res 都是 Node 原生对象,因此 webpack-dev-server 的 Express 中间件不一定能直接在 Modern.js 中使用。
如果要迁移 webpack-dev-server 中使用的 Express 中间件,可以使用以下方式,将 Express app 作为中间件传入:
import expressMiddleware from 'my-express-middleware';
import express from 'express';
// 初始化 Express app
const app = express();
app.use(expressMiddleware);
export default {
  tools: {
    devServer: {
      after: [app],
    },
  },
};
 
before
添加自定义中间件,在所有开发环境中间件前执行。
export default {
  tools: {
    devServer: {
      before: [
        async (req, res, next) => {
          console.log('before dev middleware');
          next();
        },
      ],
    },
  },
};
 
client
{
    /** 指定协议名称 */
    protocol?: string;
    /** 事件流路径 */
    path?: string;
    /** 指定监听请求的端口号 */
    port?: string;
    /** 指定要使用的 host */
    host?: string;
}
 
const defaultConfig = {
  path: '/webpack-hmr',
  // By default it is set to the port number of the dev server
  port: '',
  // By default it is set to "location.hostname"
  host: '',
  // By default it is set to "location.protocol === 'https:' ? 'wss' : 'ws'""
  protocol: '',
};
 
对应 HMR 客户端的配置,通常用于设置 HMR 对应的 WebSocket URL。
compress
是否对静态资源启用 gzip 压缩。
如果你需要禁用 gzip 压缩,可以将 compress 设置为 false:
export default {
  tools: {
    devServer: {
      compress: false,
    },
  },
};
 
devMiddleware
{
  writeToDisk: boolean | ((filename: string) => boolean);
}
 
{
  writeToDisk: (file: string) => !file.includes('.hot-update.'),
}
 
devMiddleware 配置项。当前配置是 webpack-dev-middleware 配置项的子集.
- 类型: 
Record<string, string> 
- 默认值: 
undefined 
设置自定义响应头。
export default {
  tools: {
    devServer: {
      headers: {
        'X-Custom-Foo': 'bar',
      },
    },
  },
};
 
historyApiFallback
- 类型: 
boolean | ConnectHistoryApiFallbackOptions 
- 默认值: 
false 
在需要对一些 404 响应或其他请求提供替代页面的场景,可通过 devServer.historyApiFallback 进行设置:
export default {
  tools: {
    devServer: {
      historyApiFallback: true,
    },
  },
};
 
更多选项和详细信息可参考 connect-history-api-fallback 文档。
hot
WARNING
Deprecated:该配置已废弃,请使用 dev.hmr 代替。
 
是否开启 Hot Module Replacement 热更新能力。
https
- 类型: 
boolean | { key: string; cert: string } 
- 默认值: 
false 
默认情况下,DevServer 会启用 HTTP 服务器。通过设置 devServer.https 为 true 将开启对 HTTPS 的支持,同时会禁用 HTTP 服务器。
你也可以手动传入 HTTPS 服务器所需要的证书和对应的私钥:
export default {
  tools: {
    devServer: {
      https: {
        key: fs.readFileSync('certificates/private.pem'),
        cert: fs.readFileSync('certificates/public.pem'),
      },
    },
  },
};
 
liveReload
默认情况下,当监听到文件变化时,DevServer 将会刷新页面(为使 liveReload 能够生效,devServer.hot 配置项应当禁用)。通过设置 devServer.liveReload 为 false 可以关闭该行为。
setupMiddlewares
Array<
  (
    middlewares: {
      unshift: (...handlers: RequestHandler[]) => void;
      push: (...handlers: RequestHandler[]) => void;
    },
    server: {
      sockWrite: (
        type: string,
        data?: string | boolean | Record<string, any>,
      ) => void;
    },
  ) => void
>;
 
提供执行自定义函数和应用自定义中间件的能力。
几种不同中间件之间的执行顺序是: devServer.before => unshift => 内置中间件 => push => devServer.after。
export default {
  tools: {
    devServer: {
      setupMiddlewares: [
        (middlewares, server) => {
          middlewares.unshift((req, res, next) => {
            next();
          });
          middlewares.push((req, res, next) => {
            next();
          });
        },
      ],
    },
  },
};
 
一些特殊场景需求可能需要使用服务器 API:
- sockWrite。允许向 hmr 客户端传递一些消息,hmr 客户端将根据接收到的消息类型进行不同的处理。如果你发送一个 "content-changed " 的消息,页面将会重新加载。
 
export default {
  tools: {
    devServer: {
      setupMiddlewares: [
        (middlewares, server) => {
          // 添加自定义 watcher 并在文件更新时触发页面刷新
          watcher.on('change', changed => {
            server.sockWrite('content-changed');
          });
        },
      ],
    },
  },
};
 
proxy
- 类型: 
Record<string, string> | Record<string, ProxyDetail> 
- 默认值: 
undefined 
代理请求到指定的服务上。
export default {
  tools: {
    devServer: {
      proxy: {
        '/api': 'http://localhost:3000',
      },
    },
  },
};
 
此时,/api/users 请求将会代理到 http://localhost:3000/api/users。
如果你不想传递 /api,可以通过 pathRewrite 重写请求路径:
export default {
  tools: {
    devServer: {
      proxy: {
        '/api': {
          target: 'http://localhost:3000',
          pathRewrite: { '^/api': '' },
        },
      },
    },
  },
};
 
DevServer Proxy 基于 http-proxy-middleware 实现。你可以使用 http-proxy-middleware 的所有配置项,具体可以查看文档。
DevServer Proxy 完整类型定义为:
import type { Options as HttpProxyOptions } from 'http-proxy-middleware';
type Filter = string | string[] | ((pathname: string, req: Request) => boolean);
type ProxyDetail = HttpProxyOptions & {
  bypass?: (
    req: IncomingMessage,
    res: ServerResponse,
    proxyOptions: ProxyOptions,
  ) => string | undefined | null | false;
  context?: Filter;
};
type ProxyOptions =
  | Record<string, string>
  | Record<string, ProxyDetail>
  | ProxyDetail[]
  | ProxyDetail;
 
除了 http-proxy-middleware 的选项外,还支持 bypass 和 context 两个配置项:
- bypass:根据函数的返回值绕过代理。
- 返回 
null 或 undefined 会继续用代理处理请求。 
- 返回 
false 会返回 404 错误。 
- 返回一个具体的服务路径,将会使用此路径替代原请求路径。
 
 
- context:如果你想代理多个特定的路径到同一个目标,你可以使用 context 配置项。
 
// 自定义 bypass 方法
export default {
  tools: {
    devServer: {
      proxy: {
        '/api': {
          target: 'http://localhost:3000',
          bypass: function (req, res, proxyOptions) {
            if (req.headers.accept.indexOf('html') !== -1) {
              console.log('Skipping proxy for browser request.');
              return '/index.html';
            }
          },
        },
      },
    },
  },
};
 
// 代理多个路径到同一个目标
export default {
  tools: {
    devServer: {
      proxy: [
        {
          context: ['/auth', '/api'],
          target: 'http://localhost:3000',
        },
      ],
    },
  },
};
 
watch
是否监听 mock/、server/、api/ 等目录的文件变化。