webpack-dev-server是一个封装好的webpack开发服务器,底层使用express。通常用在开发环境的webpack打包,它有以下这些作用:
- 读取webpack.config.js并使用webpack进行编译
- 默认集成一些第三方插件并可供配置,都在webpack.config.js下的devServer节点下(本节重点)
- 开启一个websocket以实现热更新
- 基于webpack-dev-middleware(opens new window)实现
- 编译输出放到内存中,不会生成真实的文件
- 开启本地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)
devServer: {// 提供静态文件目录地址// 基于express.static实现contentBase: path.join(__dirname, 'dist'),// 任意的 404 响应都被替代为 index.html// 基于node connect-history-api-fallback包实现historyApiFallback: true,// 是否一切服务都启用 gzip 压缩// 基于node compression包实现compress: true,// 是否隐藏bundle信息noInfo: true,// 发生错误是否覆盖在页面上overlay: true,// 是否开启热加载// 必须搭配webpack.HotModuleReplacementPlugin 才能完全启用 HMR。// 如果 webpack 或 webpack-dev-server 是通过 --hot 选项启动的,那么这个插件会被自动添加hot: true,// 热加载模式// true代表inline模式,false代表iframe模式inline: true, // 默认是true// 是否自动打开open: true,// 设置本地url和端口号host: 'localhost',port: 8080,// 代理// 基于node http-proxy-middleware包实现proxy: {// 匹配api前缀时,则代理到3001端口// 即http://localhost:8080/api/123 = http://localhost:3001/api/123// 注意:这里是把当前server8080代理到3001,而不是任意端口的api代理到3001'/api': 'http://localhost:3001',// 设置为true, 本地就会虚拟一个服务器接收你的请求并代你发送该请求// 主要解决跨域问题changeOrigin: true,// 针对代理httpssecure: false,// 覆写路径:http://localhost:8080/api/123 = http://localhost:3001/123pathRewrite: {'^/api' : ''}}}
源码解析
webpack-dev-server源码,把一些细枝末节去掉应该好理解很多,大部分都是基于第三方插件封装的API:
const features = {// compress// 基于compression包compress: () => {if (options.compress) {// Enable gzip compression.app.use(compress());}},// proxy// 基于http-proxy-middleware包proxy: () => {const getProxyMiddleware = (proxyConfig) => {const context = proxyConfig.context || proxyConfig.path;if (proxyConfig.target) {return httpProxyMiddleware(context, proxyConfig);}};// 多个proxy设置options.proxy.forEach((proxyConfigOrCallback) => {// http-proxy-middleware作为中间件应用到express中// 如果自己封装代理,这里很值得借鉴app.use((req, res, next) => {const proxyConfig = proxyConfigOrCallback();proxyMiddleware = getProxyMiddleware(proxyConfig);proxyMiddleware(req, res, next);})}},// historyApiFallback// 基于connect-history-api-fallback包historyApiFallback: () => {if (options.historyApiFallback) {// Fall back to /index.html if nothing else matches.app.use(historyApiFallback(fallback));}},// contentBase// 基于express.staticcontentBaseFiles: () => {if (Array.isArray(contentBase)) {contentBase.forEach((item) => {app.get('*', express.static(item));});}}}
