概述
一般情况下,在使用webpack-dev-server热更新的时候,使用的域名都是localhost/127.0.0.1之类的,都是基于http协议的。
基于nodejs对webpack入口的处理如下:
webpackConfig.entry[entryKey] = [
// 这里会发送一次请求
'webpack-dev-server/client?http://0.0.0.0:8080',
'webpack/hot/dev-server',
...webpackConfig.entry[entryKey]
];
热更新成功后,浏览器会以当前域名+端口发送一个用于热更新的请求。该请求默认域名是webpack入口配置中webpack-dev-server/client?http://0.0.0.0:8080
中问号后面的参数,如果参数中主机名是0.0.0.0
,将会被替换成self.location.hostname
。/sockjs-node
请求被webpack-dev-server服务器处理。
具体的替换规则有多种情况,可以参考webpack-dev-server中createSocketUrl的源码。
nginx + host 配置url
对于需要公众号微信授权的网页,每个公众号都可以配置两个js安全回调域名。所以网页的域名必须是公众号中配置存在的。比如原始网页访问地址是http://127.0.0.1:8080/test/abc
。公众号配置的安全回调域名是abc.com
。如果该网页需要经过微信授权(授权过程不表),那么需要通过配置让网页的域名变成abc.com
。访问地址就是abc.com/test/abc
。否则微信会报redirect_uri参数错误。
通过host配置以及nginx端口转发后,就可以通过abc.com/test/abc
访问http://127.0.0.1:8080/test/abc
了。配置如下:
- host配置,将abc.com代理到本地地址
127.0.0.1 abc.com
只配置host的话可以通过abc.com:8080/test/abc
来访问网页。这时候热更新请求的地址是http://abc.com:8080/sockjs-node/info
。
- nginx配置
server {
listen 80;
listen 443 ssl;
server_name abc.com;
ssl_certificate /usr/local/etc/nginx/abc.crt;
ssl_certificate_key /usr/local/etc/nginx/abc.key;
# 省略一些配置
# 3200是node端口
location / {
proxy_pass http://127.0.0.1:3200/;
# 其他配置
}
# 8080 是前端资源端口
location /test {
proxy_pass http://127.0.0.1:8080/test;
# 其他配置
}
}
完成这两步,就可以通过http://abc.com/test/abc
来访问网页了,并且可以通过微信授权。但是仍然是在http的协议下。在这种情况下,热更新请求的地址仍然是http://abc.com:8080/sockjs-node/info
,显然nginx没有监听8080端口以及/sockjs-node,因为8080端口已经被webpack-dev-server占用了。但是该请求仍是可以正常打开的,不经过nginx也可以,请求的就是本地资源,该请求被webpack-dev-server服务器处理了。。
在某些情况下,需要https访问,比如service-worker
,微信支付(支付目录是https协议的)等。
https协议热更新
首先在nginx中配置https://abc.com的证书,并监听443端口。上述代码中已经配置。打开https://abc.com/test/abc
连接,页面可以打开。热更新请求的地址是https://abc.com:8080/sockjs-node/info
,可以看到仍然是8080端口。因为根据createSocketUrl的源码,在入口中配置了webpack-dev-server/client?http://0.0.0.0:8080
的情况下,如果该链接query参数中有端口并且webpack-dev-server没有配置端口的情况下,就使用该端口。
这时候情况就不一样了,打不开热更新请求的地址。8080没有经过nginx,该请求请求的仍然是webpack-dev-server服务器资源。但是webpack-dev-server服务器没有配置https证书,导致无法访问到。
关键点来了。如果该请求经过nginx,再代理到本地webpack-dev-server服务器就好了。因为nginx中配置了https证书。
查了好长时间,看到一个相关issue,在webpack-dev-server5月份发布的3.4.0 (2019-05-17)release版本中,提供了sockPort,sockHost选项,用于配置热更新请求的端口和主机,官网文档中可以查到。
所以改造需要三步。
- 入口改造
webpackConfig.entry[entryKey] = [
// 这里会发送一次请求
'webpack-dev-server/client?http://0.0.0.0:8080',
'webpack/hot/dev-server',
...webpackConfig.entry[entryKey]
];
变更为
webpackConfig.entry[entryKey] = [
// 这里会发送一次请求
'webpack-dev-server/client',
'webpack/hot/dev-server',
...webpackConfig.entry[entryKey]
];
因为不要读取这里的配置了。
- webpack-dev-server添加该配置
sockPort: 443
- nginx转发
/sockjs-node
location /sockjs-node {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
简单来说,通过配置sockPort后,热更新请求变成了https://abc.com/sockjs-node/info
,443可以省略。443端口被nginx转发到本地http协议的8080端口,最终请求的是webpack-dev-server服务器。如图:
这里几乎同时发了两个请求。经过测试,可以改为只发一个。在上面的配置中去掉webpack-dev-server/client
这一行,并且webpack-dev-server添加配置sockHost: 'abc.com'
即可。