目的
远程调试页面的工具有很多, 像 Charles,spy-debugger 等等, 但是上次介绍 ZanProxy 的文章中已经把这些工具都扔掉了...... 所以要想一个办法能实现远程调试, 但是又不打脸, 于是就有了使用 ZanProxy 插件的方案, 本文将细细道来.
插件
根据官方文档编写插件 . ZanProxy https://youzan.github.io/zan-proxy/book/plugin/code.html 一节, 可以得知:
ZanProxy 插件是是一个 npm 包, 这个 npm 包需要导出一个类, 这个类要实现两个方法: proxy 和 manage, 除此之外没有任何特殊要求. proxy 方法用于请求的处理, manage 方法用于插件的配置.
- // 最简单的结构框架
- module.exports = class RemoteDebugPlugin { proxy() {}
- manage() {}
- }
需求
前端的远程调试, 无非就是希望能将远端的资源切换成本地的, 以及能够看到开发者工具中的一系列信息 (如控制台输出, 网络请求等). 那么资源的切换可以通过代理去实现 (之前的文章中已经讲过, 通过 ZanProxy 的 Hosts 管理和 Http 转发), 而查看控制台输出的需求, 只有当页面的载体是 PC 端的浏览器才可以 (任何端的 webview 以及移动端的任何都不行). 不使用第三方工具, 我们能做的就只有在页面上模拟一个开发者工具, 通过大量的 search 和 compare, 最终有两家入选 --vConsole & eruda.
- vConsole vs eruda
- vConsole
腾讯开发的
一个轻量, 可拓展, 针对手机网页的前端开发者调试面板
. 官方文档 https://github.com/Tencent/vConsole/blob/dev/README_CN.md
注入后页面右下角将会多出一个绿色的 vConsole 悬浮按钮:
eruda
Eruda 是一个专为手机网页前端设计的调试面板, 类似 DevTools 的迷你版, 其主要功能包括: 捕获 console 日志, 检查元素状态, 捕获 XHR 请求, 显示本地存储和 Cookie 信息等等. 官方文档 https://github.com/liriliri/eruda/blob/master/doc/README_CN.md
注入后页面右下角将会多出一个图标为齿轮浮层:
各取所需吧, 感觉这两个都很不错. 但由于 vConsole 的文档更像详细, 所以我在这里选择了 vConsole. 下文均基于 vConsole 进行介绍, 如需使用 eruda, 改动是非常小的, 将 vConsole 的配置部分按照文档修改成 eruda 的即可, 对于 ZanProxy 是无感的.
实现 proxy 方法
不论是使用 vConsole 还是 eruda, 其本质都是在页面内添加一段 script 脚本, 所以我们需要拦截 html 文件, 在 header 部分 插入对应的 script 脚本后返回给客户端使用.
- const { byteLength } = require('byte-length')
- const zlib = require('zlib')
- proxy() {
- return async (ctx, next) => {
- const contentType = ctx.res.getHeader('content-type')
- const contentEncoding = ctx.res.getHeader('content-encoding')
- if(contentType === 'text/html') {
- // 只拦截 html 文件
- ctx.res.body.on('end', () => {
- let contentString = ctx.res.body
- // 如果是 gzip 的话, 需要解压
- if(contentEncoding === 'gzip') {
- contentString = zlib.gunzipSync(contentString)
- ctx.res.removeHeader('content-encoding')
- }
- contentString = contentString.toString()
- // 找到插入点 (head 闭合标签前, 第一个 script 标签前, 取最靠前的一个)
- const headEndIndex = contentString.indexOf('</head>')
- const firstScriptIndex = contentString.indexOf('<script>')
- const injectIndex = Math.max(...[headEndIndex, firstScriptIndex].filter(i => i> -1))
- if(injectIndex> -1) {
- // 插入所需要的脚本代码
- const vconsoleScript = '<script src="https://cdn.bootCSS.com/vConsole/3.2.0/vconsole.min.js"></script><script>new VConsole();</script>';
- const result = contentString.slice(0, injectIndex) + vconsoleScript + contentString.slice(injectIndex, contentString.length)
- ctx.res.setHeader('content-length', byteLength(result))
- ctx.res.body = result
- }
- })
- }
- await next()
- }
- }
实现 manage 方法
manage 方法是为了提供页面来对当前插件进行管理. 按照上面方式实现的
zan-proxy-remote-debug-plugin
插件, vConsole 会永久在所有的 html 页面中展示, 所以我们需要通过 manage 方法在页面中对 vConsole 的展示与否进行控制.
- const Koa = require('koa')
- const Static = require('koa-static')
- const Router = require('koa-router')
- const BodyParser = require('koa-bodyparser')
- const path = require('path')
- const jsonfile = require('jsonfile')
- const os = require('os')
- // 配置文件存放路径
- const configFilePath = `${os.homedir()}/.front-end-proxy/plugins/zan-proxy-remote-debug-plugin.config.js`
- class RemoteDebugPlugin {
- constructor() {
- this.vconsole = true
- // 读取配置文件覆盖默认配置
- const config = jsonfile.readFileSync(configFilePath)
- config && (this.vconsole = config.vconsole)
- }
- proxy() { /* 此处忽略 proxy 的实现 */}
- manage() {}
- }
- manage() {
- // manage 方法需要返回一个 Koa 实例
- const app = new Koa()
- // 我们将静态资源放置在 static 目录下
- app.use(Static(path.resolve(__dirname, './static')))
- app.use(BodyParser())
- // 通过 router 接收请求, 修改配置
- const router = new Router()
- // 列出配置详情
- router.get('/config', async (ctx) => {
- ctx.body = { vconsole: this.vconsole}
- })
- // 修改 vConsole 启用状态
- router.post('/vconsole', async (ctx) => {
- this.vconsole = ctx.request.body.enable
- jsonfile.writeFileSync(configFilePath, { vconsole: this.vconsole })
- ctx.body = { vconsole: this.vconsole }
- })
- app.use(router.routes())
- app.use(router.allowedMethods())
- return app
- }
既然已经有了配置文件, 那么我们需要改造一下 proxy 方法, 通过配置来确定是否需要执行页面请求的拦截.
if(contentType === 'text/html') {}
修改为
if(this.vconsole && contentType === 'text/html') {}
实现 manage 页面
manage 页面其实是上面 Koa 引用的一个静态资源
./static/index.html
, 我们需要实现这个页面.
- <input type="checkbox" id="vconsole"> 启用 vConsole
- <script>
- const configUrl = '/plugins/zan-proxy-remote-debug-plugin/config'
- fetch(configUrl).then(res => res.json()).then(config => {
- // 获取配置文件中的配置并设定
- const vConsoleSwitcher = $('#vconsole')
- vConsoleSwitcher.prop('checked', config.vconsole)
- // 用户修改配置时进行同步
- vConsoleSwitcher.change(function (e) {
- fetch('/plugins/zan-proxy-remote-debug-plugin/vconsole', {
- method: 'post',
- body: JSON.stringify({ enable: e.target.checked }),
- headers: { 'Content-Type': 'application/json' }
- })
- })
- })
- </script>
完成后的 manage 页面大概长这样:
收尾
上面的步骤都完成以后, 将代码以 npm 包的形式发布到任意一个 registry 就可以了, 在 ZanProxy 的插件管理页面添加一个插件, 填写好包名和 registry, 尽情享受你的调试插件吧!~ 当然也可以直接用我写好且已经发布了的插件, 包名:
zan-proxy-remote-debug-plugin
,registry 空着不填就可以 (默认是 https://registry.npmjs.org/).
来源: https://juejin.im/entry/5b6c2c6b5188251b1a7b65ba