大家做移动端 H5 需求或查找问题时, 对下面这些场景应该不陌生~
- 页面依赖了 js bridge 导致网页在电脑浏览器上无法直接进行调试,没有安卓手机的同学需要借用安卓测试机进行开发调试
- 手机死活连不上 chrome://inspect (部分低版本安卓系统 webview 不支持)
- 某某机型页面展示异常 但是公司没有这样的机型 只能使用云手机进行修复验证,云手机并不能连接到我的 inspect 页面
- ios 使用 safari 进行调试时,需要打 debug 包,如果 debug 人数满了,需要借 ios 测试机
- 第三方应用内的小程序 Webview、公众号可能不支持 ADB
一个简单舒适的 WebView 开发调试环境,应该满足下面 3 个条件:
- 支持远程设备 无须手机和电脑连接
- 所见即所得 本地开发的内容能实时显示在真机上
- ios 安卓 通用
Q: 假设我们有一款 APP,APP 内嵌入了一个 H5 页面,页面的地址是test.com, 那么有没有一种可能,当我们打开 APP 访问test.com的时候,可以返回我们本地的服务localhost:8000呢?
如果可以,是不是刚好就满足了上面的 3 个条件,支持远程设备、所见即所得、ios/安卓通用。
内网穿透就可以实现这样的需求
内网穿透
内网穿透,即 NAT(Network Address Translator)穿透,是指计算机在内网(局域网)内使用私有 IP 地址,在连接外网(互联网)时使用全局 IP 地址的技术。简单点说就是能让外网的设备找到处于内网的设备,从而实现数据通信。
比如: 你在本地启动了一个 localhost:8000 的服务,这个时候外网是无法访问的,而内网穿透可以将本地的 localhost:8000 的服务暴露出去,让外网能够有能力访问到你的本地服务。
目前市面上有很多工具,较为知名的开源工具有Ngrok、FRP、fcn、goProxy等
使用 FRP 将本地 HTTP/HTTPS 服务提供给外网
FRP
FRP 是一种快速反向代理,可帮助您将 NAT 或防火墙后面的本地服务器暴露给 Internet。目前,它支持TCP和UDP,以及HTTP和HTTPS协议,可以通过域名将请求转发到内部服务。
安装
首先根据操作系统 从 FRP下载页面分别下载对应的服务端及客户端 FRP 安装文件
服务端
下载
wget https://github.com/fatedier/frp/releases/download/v0.44.0/frp_0.44.0_linux_amd64.tar.gz
解压
tar -xvf frp_0.44.0_linux_amd64.tar.gz
客户端
找到对应的系统进行下载
Mac frp_0.44.0_darwin_amd64.tar.gz
Windows frp_0.44.0_windows_amd64.zip
文件说明
frps.ini: 服务端配置文件
frps: 服务端软件
frpc.ini: 客户端配置文件
frpc: 客户端软件
配置和使用
服务端
frps.ini
[common]
bind_port = 8000 #自定义frp服务端端口
dashboard_port = 8080 #自定义控制台端口
token = token #服务端和客户端的连接密钥 自己设定 和frpc.ini的设置保持一致
dashboard_user = admin #自定义控制台用户名
dashboard_pwd = admin #自定义控制台密码
vhost_http_port = 8088 #http访问端口
启动
./frps -c ./frps.ini
# 后台启动
nohup ./frps -c ./frps.ini &
注意服务器设置中需要打开对应的端口 阿里云服务器开启 8000 ~ 9000 端口
客户端
将刚刚下载的客户端文件进行解压,在前端项目目录下新建 bin 文件夹,将 frp 文件放入 bin 文件夹内
frpc.ini
[common]
server_addr = test.com #frps服务器公网 IP/域名
server_port = 8000 #服务器端口 与frps bind_port保持一致
token = token #服务端和客户端的连接密钥 自己设定 和frps.ini的设置保持一致
[web]
type = http #http | https | udp | ssh
local_ip = 127.0.0.1 #本地服务IP
local_port = 8001 #本地服务端口
use_encryption = true #加密
use_compression = true #压缩
启动
./bin/frp/frpc -c ./bin/frpfrpc.ini
如果看到如下页面 说明服务已经启动成功
这个时候就可以通过访问之前在 frps 中配置的的域名加端口号(test.com:8088)访问到自己本地的 web 服务了
虽然已经实现了外网访问本地服务,但实际开发过程中,不可能只有一个开发人员,所以我们还需要修改一下 frp 配置,使得多人可以共享一台 frps 服务器
修改 frps.ini 添加 subdomain_host
[common]
bind_port = 8000 #frp服务端端口
dashboard_port = 8080 #控制台端口
token = token #服务端和客户端的连接密钥 自己设定 和frpc.ini的设置保持一致
dashboard_user = admin #控制台用户名
dashboard_pwd = admin #控制台密码
vhost_http_port = 8088 #http访问端口
subdomain_host = test.com #多人共享一台 frps 服务器时,frps服务的顶级域名
重启 frps 服务
./frps -c ./frps.ini
# 如果之前是后台启动需要先杀掉之前的进程
ps -ef
kill -9 进程号
nohup ./frps -c ./frps.ini &
前端项目 frp 文件夹下新建 frpc.template.ini 添加 subdomain 自定义子域名,区分不同开发人员
[common]
server_addr = test.com
server_port = 8000
token = token
[${name}] #自定义name
type = http
local_ip = 127.0.0.1
local_port = ${port} #自定义端口
use_encryption = true
use_compression = true
subdomain = ${name} #自定义子域名 区分不同开发/服务
注意域名需支持子域名解析
阿里域名解析配置示列:
前端项目下新建 createFrp.js 这里主要是获取开发人员电脑的用户名,填写 frpc.template.ini 模版,并启动 frp 和前端项目本地服务
const os = require('os'); // 获取系统信息
const execa = require('execa'); // 执行shell
const fs = require('fs'); // 文件系统
const path = require('path'); // 路径
// 获取自定义port
let [, httpPort = 8000] =
process.argv
.slice(2)
.join(' ')
.match(/-p\s*(\d+)/) || [];
// 获取客户端系统用户名 用来区分不同的开发 这里可以使用其他方式 只要保证唯一就可以
function getFrpcName() {
return os.userInfo().username;
}
function configFrpc() {
const username = getFrpcName();
// 使用frpc.template.ini 创建我们的frpc.ini文件
fs.writeFileSync(
path.join(__dirname, 'bin/frp/frpc.ini'),
fs
.readFileSync(path.join(__dirname, 'bin/frp/frpc.template.ini'))
.toString()
.replace(/\$\{name\}/g, username)
.replace(/\$\{port\}/g, httpPort),
);
// 启动frpc服务
execa('./bin/frp/frpc', ['-c', './bin/frp/frpc.ini'], {
cwd: process.cwd(),
stdio: 'inherit',
});
// 启动next服务,这里使用了next作为演示,其他项目需要修改启动命令
execa(`./node_modules/next/dist/bin/next`, ['dev', '-p', httpPort], {
cwd: process.cwd(),
stdio: 'inherit',
});
}
configFrpc();
package.json
修改启动命令
"scripts": {
"dev": "node creatFrp -p 8001",
}
启动前端项目
npm run dev
or
yarn dev
假设有小明和二狗两个开发人员
这时小明可以使用 xiaomin.test.com:8088 访问到自己的本地服务
二狗可以使用 ergou.test.com:8088 访问到自己的本地服务
到这里已经实现了外网访问本地服务、多人共享一台 frp,回到刚刚我们的假设,我们希望的是在访问 test.com 的时候可以返回我们本地 localhost 的服务,但是小明现在实际只能通过 xiaomin.test.com:8088 才能访问本地服务,这时候我们就需要使用到Nginx的转发功能,在业务服务器上配置 Nginx 转发,当请求带上了proxy_subdomain这个 cookie 的时候,我们就将请求转发到${proxy_subdomain}.test.com上
比如小明带了一个proxy_subdomain=xiaomin的 cookie 访问test.com,这时候我们就将请求转发到xiaomin.test.com:8088上,这样小明就连上了自己的本地服务
配置 nginx
vim /etc/nginx/nginx.conf
nginx.conf 由于我的 frp 服务就部署在外网服务器上,所以转发使用的域名是 127.0.0.1
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name test.com;
location / {
set $subdomain '';
if ($http_cookie ~* "proxy_subdomain") {
# 有proxy_subdomain cookie的时候转发到frp服务上
proxy_pass http://127.0.0.1:8088; #frp服务器域名
set $subdomain ${cookie_proxy_subdomain}.;
}
proxy_set_header Host ${subdomain}test.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://127.0.0.1:8888/; #没有cookie的时候 访问正常服务
}
}
当 cookie 中写入 proxy_subdomain,访问线上地址时,Nginx 就会根据设置的不同 proxy_subdomain 返回对应的本地服务
最后我们只需要加上调试面板,方便我们查看 DOM 结构、网络请求,console 信息等,这里可以直接使用移动端调试工具vConsole
打通最后一公里
Q: chrome://inspect 是如何实现的?,我们能实现 chrome://inspect 同样的效果吗?
像 weinre这样的远程调试工具,用最新的chrome devtools 前端替换 web 检查器。
如下图所示,利用 内网穿透 配合 chii 就可以实现和 chrome://inspect 相同的能力,左边是 pc 上的 devTools,右边是手机上访问的页面
安装 chii
npm install chii -g
frpc.template.ini
在 frpc.template.ini 新增一个 chii 的 http 内网穿透配置
[chii]
type = http
local_ip = 127.0.0.1
local_port = 8999
use_encryption = true
use_compression = true
subdomain = chii${name}
creatFrp configFrpc 函数下新增脚本命令
execa(`chii`, ['start', '-p', 8999], {
cwd: process.cwd(),
stdio: 'inherit',
});
启动
npm run dev
or
yarn dev
访问 chii 服务 浏览器打开 localhost: 8999
将页面中提示的 js 放入前端项目中
You can use this script to inject the chii target code into your web page.
localhost:8999/target.js
将这里的 target.js 的域名换成刚刚配置的 chii 内网穿透地址 如:chiixuxiaohua.test.com:8088/target.js\ 在前端项目中引入<script defer src="http://chiixuxiaohua.test.com:8088/target.js" />
访问业务域名就可以看到如下页面 点击 inspect 就可以看到调试面板
现在就实现了多人共用、所见即所得、支持远程设备、支持 ios/安卓的 webView 开发调试环境。剩下的就只需要判断是开发者的时候,在页面上提供一个悬浮的开发者按钮,点击后可自定义代理地址(proxy_subdomain)
总结
调试设备 webview 确实麻烦,首先需要安卓手机连接电脑,进入 chrome://inspect 页面,然后找到对应的 webview,点击inspect按钮进入调试页,将地址替换成本地服务,并且不支持远程设备,不支持 ios。
通过内网穿透 + vConsole/chii 后,只需要打开 webview,输入代理地址就可以进行开发了,并且无论是 ios、安卓、第三方应用、小程序、大屏、低版本安卓、云端设备...等等,只要是网页就可以通过这种方式进行开发调试。快速定位解决问题。