webpack proxy ,就是 webpack 提供的解决跨域的方案。其基本行为是接受客户端发送的请求后转发给其他的服务器,目的是为了解决在开发模式下的跨域问题。
webpack中的proxy 工作原理是利用了 http-proxy-middleware
这个http 代理中间件,实现将请求转发给其他的服务器。
如下:在开发阶段,本地地址是 Http://loaclhost:3000
, 该浏览器发送一个前缀带有 /api 标识的向服务器请求数据,但是这个服务器只是将这个请求转发给另一台服务器:
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use('/api', proxy({target: 'http://www.test.org', changeOrigin: true}));
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.test.org/api/foo/bar
在开发阶段,webpack-dev-server 会自动启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 localhost 的一个端口上的,而后端服务器又是运行在另一个地址上.。
所以在开发阶段中,由于浏览器的同源策略,当本地浏览器访问后端服务器的时候就会出现跨域请求资源的问题。
通过设置 webpack proxy 实现代理请求后,相当于浏览器和服务器之间添加了一个代理服务器。
当本地发送请求的时候,中间服务器会接受这个情求,并将这个请求转发给目标服务器(也就是后端服务器),目标服务器返回数据后,中间服务器又会将数据返回给浏览器,当中间服务器将数据返回给服务器的时候,它们两者是同源的,并不会存在跨域的问题。
服务器和服务器之间是不会存在跨域资源的问题的。
webpack 提供代理服务器的工具是webpack-dev-server
,但只适用于开发阶段,线上要实现代理一般通过 nginx 来配置。
webpack 中的服务器工具 webpack-dev-server,实质上是启动了一个 express 服务器。
proxy 代理是利用 http-proxy-middleware
这个http代理中间件实现将请求转发给其他服务器。
(vite 是用的 http-proxy,其实 http-proxy-middleware 也是基于 http-proxy 的)
背后其实都是使用 node 来启动 server 服务器,这也是为什么我们常说这种代理只能在开发阶段使用,因为 build 生产包时我们并不会打包一个 node 服务器进去,线上要实现代理一般直接通过 nginx 来配置。
可以在webpack 配置对象属性中通过 devServer 属性来配置:
// ./webpack.config.js
const path = require('path')
module.exports = {
// ...
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
proxy: {
'/api': {
target: 'https://api.github.com'
}
}
// ...
}
}
它的目的是设置代理来解决跨域访问的问题。举例:
我们的一个api请求地址(也就是服务器地址)是 http://localhost:8888
,
但是本地启动服务器的域名是 http://localhost:8000
,
这个时候发送网络请求就会出现跨域的问题(端口不同)。
所以将请求先发送到一个代理服务器,代理服务器和API服务器没有跨域的问题,就可以解决我们的跨域问题了。
module.exports = {
//...
devServer: {
proxy: {
'/api': {// 以 /api 开头的请求,会转发到下面的 target 配置
target: 'http://localhost:8888',// 目标服务器
pathRewrite: {
"^/api": "", // 重写路径为空,即请求路径中没有/api字符串
"^/api": "/abcd" // 重写路径为abcd,即将请求路径中的/api字符串替换成/abcd
},
secure: false,// https接口,需要配置这个参数
changeOrigin: true,// 将请求头中的host 配置为 target
},
},
host: '0.0.0.0', //用于指定devDerve使用的host,如果你希望服务器外部可以访问,设定如 host: '0.0.0.0'
https: true, // 默认情况下dev-server使用http协议,通过配置可以支持https,
// 注意:默认使用自签名证书,也可以配置自己的证书
// https: {
// ca: './path/to/server.pem',
// pfx: './path/to/server.pfx',
// key: './path/to/server.key',
// cert: './path/to/server.crt',
// passphrase: 'webpack-dev-server',
// requestCert: true,
// },
// 开启热模块替换
hot: true,
},
};
target:
表示的是代理到的目标地址(也就是要访问的服务器地址),比如 /api
会被代理到 http://localhost:8888/api
pathRewrite
:默认情况下,我们的 /api
也会被写入到URL中,即:http://localhost:8888/api
, 如果希望删除,可以使用pathRewrite
,比如:"^/api": ""
,那么/api/user
=> http://localhost:8888/user
,地址中没有/api,因为重新路径是空的;"^/api": "/abcd"
,那么/api/user
=> http://localhost:8888/abcd/user
,即将请求路径中的/api
字符串替换成/abcd
secure
: 默认情况下,不接受在 HTTPS 上运行且证书无效的后端服务器。 https接口,需要配置这个参数为false;headers
中host地址,一般设置为trueproxy
配置中 changeOrigin: true
,// 将请求头中的host 配置为 target。
那为什么要更改请求头中的host呢?
因为一台服务器上可以部署多个项目,当我们发送请求时,DNS 会将域名解析为 IP 地址,此时,也就确定了我们目标服务器,那如何确定我们要访问的是哪个项目呢?这就需要用到 host 字段了。
请求头中Host用来指定请求的 域名/ip地址和端口号,通常用于指定服务器的地址,端口号可以省略,省略时默认为80端口。
此时我们将host指定为a.com就会访问到淘宝服务了。
所以如果一台服务器上部署多个项目时,我们需要更改请求头中的host,来指定我们要访问的项目。
一台服务器只有一个项目时,我们可以不用更改请求头中的host,因为DNS解析后,就确定了我们要访问的项目。
但为了以后用起来方便,就统一将这个配置项changeOrigin设置为true。
devServer.host
配置项?于配置 DevServer 服务监听的地址,默认使? 8080 端?。 如果 8080 端? 已经被其它程序占有就使? 8081,如果 8081 还是被占?就使? 8082,以此类推
例如:
你想要局域?中的其它设备访问你本地的服务,可以在启动 DevServer 时带上 --host 0.0.0.0
,
也可以在webpack
配置项devServer.host
中设置0.0.0.0
host 的默认值是 127.0.0.1
即只有本地可以访问 DevServer 的 HTTP 服务。
localhost 和 0.0.0.0 的区别:
- localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1。
- 127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收。
正常的数据库包经过 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ,而回环地址,是在网络层直接就被获取到了,是不会经过数据链路层和物理层的。
比如:
我们监听 127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的。
0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序,比如我们监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的
devServer.port
设置监听的端口,默认情况下是8080,不能设置为null,可以设置自动为auto
module.exports = {
//...
devServer: {
port: 8080,
},
};
devServer.open
告诉 dev-server 在服务器已经启动后打开浏览器。设置其为 true 以打开你的默认浏览器。
module.exports = {
//...
devServer: {
open: true,
//在浏览器中打开指定页面:open: ['/my-page']
//提供要使用的浏览器名称,而不是默认名称
// open: {
// app: {
// name: 'google-chrome',
// },
// },
},
};
devServer.compress
告诉 dev-server 在服务器端启用 gzip 压缩,用于减少服务器向前端传输的数据量,提高浏览的速度。
module.exports = {
//...
devServer: {
compress: true,
},
};