Shermie-proxy:基于Node.js的脚本化HTTP/HTTPS代理调试工具实战指南
1. 项目概述与核心价值
最近在折腾一些本地开发环境下的网络请求调试和抓包,发现一个挺有意思的开源项目kxg3030/shermie-proxy。这本质上是一个基于 Node.js 实现的 HTTP/HTTPS 代理服务器,但它的定位非常清晰:专为开发者本地调试和网络请求分析而生。如果你经常需要查看前端应用发出的请求详情、分析 API 接口数据、模拟慢速网络,或者对手机 App 的请求进行抓包,但又觉得像 Charles、Fiddler 这类图形化工具太重,或者需要一些自动化集成,那么这个轻量级的命令行代理工具很可能就是你的菜。
简单来说,Shermie-proxy 就像一个“透明中间人”,它在你本地启动一个代理服务,然后将你电脑或手机的网络流量引导到这个服务上。它不仅能记录下所有经过的 HTTP/HTTPS 请求和响应的详细信息(包括头部、Body 等),还能让你动态地修改这些请求和响应,或者模拟各种网络条件。它的核心优势在于“脚本化”和“可编程”。所有的抓包逻辑、修改规则都可以通过 JavaScript 代码来定义和扩展,这为自动化测试、定制化调试场景打开了大门。对于前端开发者、后端 API 提供者、移动端开发,甚至是测试工程师,掌握这样一个工具,能极大地提升定位网络问题的效率。
2. 核心架构与工作原理拆解
2.1 代理服务的基本模型
要理解 Shermie-proxy,首先得明白一个 HTTP/HTTPS 代理是如何工作的。想象一下邮局系统:你的信件(网络请求)原本是直接寄给收件人(目标服务器)。现在你设置了一个代理,就相当于你把所有信件都先交给一个“代理邮局”(Shermie-proxy),由它来检查、记录,然后再帮你寄出去,并将回信(服务器响应)同样经过它检查后交还给你。
在技术层面,Shermie-proxy 主要处理两种协议:
- HTTP 代理:相对简单。客户端(如浏览器)明确知道代理的存在,会直接向代理服务器发送完整的 HTTP 请求(包含
Host头等信息)。代理服务器解析这个请求,然后代表客户端向目标服务器发起新的连接,获取响应后再返回给客户端。 - HTTPS 代理:这是关键,也是难点。HTTPS 要求端到端加密,代理无法直接看到明文内容。这里用到了“中间人(MITM)”技术。当客户端向代理发起
CONNECT请求(例如CONNECT github.com:443 HTTP/1.1)时,代理会与目标服务器建立 TCP 连接。一旦隧道建立,客户端和服务器会开始 TLS 握手。此时,代理需要“介入”:它动态生成一个针对目标域名的假证书,并与客户端完成 TLS 握手;同时,它自己再与目标服务器进行真正的 TLS 握手。这样,代理就拥有了两端的加密会话密钥,可以对流量进行解密、记录和再加密,实现了对 HTTPS 流量的透明解析。
注意:正因为使用了 MITM 技术,你必须在客户端(浏览器、手机)上安装并信任 Shermie-proxy 自己生成的根证书(CA Certificate)。否则,客户端会检测到证书不匹配(由代理签发的假证书),从而中断连接并报安全错误。这是所有 HTTPS 抓包工具的通用原理,并非本项目独有。
2.2 Shermie-proxy 的模块化设计
Shermie-proxy 的代码结构体现了清晰的模块化思想,这使得它易于理解和扩展。其核心通常包含以下几个部分:
- 代理服务器核心:负责监听端口,接受客户端的代理连接请求。它根据协议(HTTP 或 HTTPS)将连接路由到不同的处理模块。
- 请求/响应拦截器:这是业务逻辑的核心。它提供了生命周期钩子(Hooks),允许开发者注入自定义的 JavaScript 代码。常见的钩子包括:
onRequest:在代理将请求转发给目标服务器之前触发。你可以在这里修改请求的 URL、方法、头部、或 Body。onResponse:在代理收到目标服务器的响应之后,返回给客户端之前触发。你可以在这里修改响应的状态码、头部、或 Body。onError:在请求过程中发生错误时触发。
- 证书管理模块:负责生成和管理用于 HTTPS 中间人攻击的根证书以及各个域名的动态叶证书。它需要处理证书的创建、缓存、以及提供给客户端下载根证书的接口。
- 日志与存储模块:负责将拦截到的请求和响应信息以结构化的方式(如 JSON)输出到控制台,或者存储到文件、数据库中,便于后续分析。
- Web UI 管理界面(可选):一些高级版本或社区插件可能会提供一个简单的 Web 界面,用于实时查看请求列表、过滤搜索、重放请求等,这比单纯看命令行日志更友好。
这种设计模式使得 Shermie-proxy 不仅仅是一个抓包工具,更是一个可编程的代理网关框架。你可以基于它快速构建出符合自己特定需求的调试工具。
3. 从零开始的环境搭建与配置
3.1 基础环境准备
首先,你需要一个 Node.js 运行环境。建议安装最新的 LTS(长期支持)版本,这能保证最好的兼容性和稳定性。你可以从 Node.js 官网下载安装包,或者使用nvm(Node Version Manager)这类工具来管理多个版本。
安装完成后,打开终端,通过以下命令验证:
node --version npm --version正常显示版本号即表示环境就绪。
接下来,获取 Shermie-proxy 的代码。由于它是一个开源项目,通常你需要克隆其 Git 仓库:
git clone https://github.com/kxg3030/shermie-proxy.git cd shermie-proxy然后安装项目依赖:
npm install如果项目提供了全局命令行工具,你可能还需要进行全局安装(通常使用npm install -g .或根据项目文档说明)。
3.2 核心配置详解
Shermie-proxy 的启动和行为通常由一个配置文件(如config.js)或启动参数来控制。理解这些配置项是高效使用它的关键。
一个典型的配置文件可能包含以下内容:
module.exports = { // 代理服务器监听的端口 port: 8899, // 是否启用HTTPS代理(即支持CONNECT方法) ssl: true, // 证书相关配置 cert: { // 根证书和私钥的存放路径,如果不存在会自动生成 caCertPath: './cert/ca.crt', caKeyPath: './cert/ca.key.pem', }, // 请求/响应的拦截规则脚本路径 rule: './myRule.js', // 是否静默运行(减少控制台输出) silent: false, // 是否记录请求体/响应体(可能影响性能,对于大文件慎用) recordBody: true, // 排除某些域名或IP不走代理(可用于绕过对本地服务或某些敏感地址的代理) exclude: ['127.0.0.1', 'localhost', '192.168.1.100'], // WebSocket代理支持 websocket: true, };关键配置解析:
port:选择一个不常用的高端口号(如 8899, 8888),避免与现有服务冲突。ssl与cert:这是 HTTPS 抓包的开关和钥匙。首次运行且ssl为true时,工具会在cert指定路径生成一对自签名的根证书(CA)。你必须将生成的ca.crt文件导入到你的操作系统或浏览器的“受信任的根证书颁发机构”中。这是让浏览器信任代理所签发假证书的唯一方法。rule:这是 Shermie-proxy 的灵魂。它指向一个你自己编写的 JS 文件,在这个文件里你可以定义所有的拦截和修改逻辑。exclude:非常实用的配置。比如你本地跑了一个后端服务在localhost:3000,你肯定不希望前端请求这个地址的流量也被代理抓取和修改,否则可能导致循环代理或连接问题。把它加入排除列表即可。
3.3 客户端代理设置
启动 Shermie-proxy 服务后,它只是一个在本地127.0.0.1:8899监听的服务。你需要告诉你的应用程序将流量发送给它。
- 全局系统代理:在 macOS 或 Windows 的网络设置中,手动设置 HTTP 和 HTTPS 代理为
127.0.0.1:8899。这种方法对所有应用生效,但可能会影响一些不使用系统代理的应用(如命令行 curl 的某些情况)。 - 浏览器代理:使用浏览器插件(如 SwitchyOmega)进行配置,可以灵活地针对不同网址模式切换代理或直连,不影响其他系统应用。
- 命令行工具:为
curl、wget等命令设置代理环境变量:export http_proxy=http://127.0.0.1:8899 export https_proxy=http://127.0.0.1:8899 # 然后运行的curl命令就会经过代理 curl https://api.example.com - 移动端抓包:确保手机和电脑在同一局域网。在手机的 Wi-Fi 设置中,找到当前网络,配置代理为“手动”,填入电脑的局域网 IP 地址和代理端口(如
192.168.1.10:8899)。同时,你还需要在手机浏览器中访问http://<电脑IP>:<代理端口>/(例如http://192.168.1.10:8899),以下载并安装根证书(通常 Shermie-proxy 会提供这个下载入口)。安装后,记得在手机的证书设置里将其标记为“受信任”。
实操心得:我强烈推荐使用“浏览器插件 + 规则配置”的方式。将日常浏览的流量交给系统代理或浏览器默认代理,而只将需要调试的特定域名(如你的开发环境
dev.your-app.com)的流量定向到 Shermie-proxy。这样既保证了调试的针对性,又不会干扰正常的网页浏览和其他网络活动。
4. 核心功能实战:编写拦截规则脚本
rule配置项指向的 JS 文件,是你发挥创造力的地方。这个文件需要导出一个包含特定生命周期方法的对象。我们通过几个经典场景来学习如何编写规则。
4.1 场景一:记录与诊断 API 请求
假设你在调试一个前端应用,想看看它到底向后端发了什么请求,以及返回了什么数据。
// myRule.js - 基础日志记录 module.exports = { /** * 请求即将发出时触发 * @param {Object} requestInfo 请求信息对象 * @param {Function} callback 回调函数,用于修改请求或直接响应 */ onRequest: async (requestInfo, callback) => { const { url, method, headers, body } = requestInfo; console.log(`[请求] ${method} ${url}`); console.log('请求头:', JSON.stringify(headers, null, 2)); if (body) { // 注意:body可能是Buffer、字符串或JSON对象,需谨慎处理 console.log('请求体:', body.toString('utf8').substring(0, 500)); // 只打印前500字符 } // 必须调用callback,不传参数表示继续正常转发请求 callback(); }, /** * 收到服务器响应时触发 * @param {Object} responseInfo 响应信息对象 * @param {Function} callback 回调函数,用于修改响应 */ onResponse: async (responseInfo, callback) => { const { statusCode, headers, body, request } = responseInfo; console.log(`[响应] ${request.method} ${request.url} -> ${statusCode}`); console.log('响应头:', JSON.stringify(headers, null, 2)); if (body && headers['content-type']?.includes('application/json')) { try { const jsonBody = JSON.parse(body.toString('utf8')); console.log('响应体(JSON):', JSON.stringify(jsonBody, null, 2)); } catch (e) { console.log('响应体(原始):', body.toString('utf8').substring(0, 1000)); } } // 继续将(可能修改后的)响应返回给客户端 callback(); }, };这个脚本会将每个请求和响应的关键信息打印到控制台。对于 JSON 格式的响应,它会进行美化输出,便于阅读。
4.2 场景二:修改请求与响应(Mock 数据)
这是本地开发中最常用的功能之一:修改 API 返回的数据,或者改变请求参数。
// myRule.js - 修改请求与Mock数据 module.exports = { onRequest: async (requestInfo, callback) => { const { url, method } = requestInfo; // 场景1:修改请求路径(重定向API) if (url.includes('/api/old-endpoint')) { requestInfo.url = requestInfo.url.replace('/api/old-endpoint', '/api/new-endpoint'); console.log(`重写请求路径至: ${requestInfo.url}`); } // 场景2:修改请求头(例如添加认证Token) if (url.includes('/api/secure')) { requestInfo.headers['Authorization'] = 'Bearer your-mock-token-here'; } // 场景3:修改POST请求的JSON Body if (method === 'POST' && url.includes('/api/submit') && requestInfo.body) { try { const bodyObj = JSON.parse(requestInfo.body.toString('utf8')); bodyObj.timestamp = Date.now(); // 注入时间戳 bodyObj.env = 'development'; // 注入环境标识 requestInfo.body = Buffer.from(JSON.stringify(bodyObj), 'utf8'); // 记得更新Content-Length头部,但Shermie-proxy可能会自动处理 // requestInfo.headers['content-length'] = Buffer.byteLength(requestInfo.body); } catch (e) { /* 忽略非JSON Body */ } } callback(); }, onResponse: async (responseInfo, callback) => { const { statusCode, headers, body, request } = responseInfo; // 场景1:Mock特定接口的响应数据 if (request.url.includes('/api/user/profile')) { // 直接返回一个自定义的响应,不向真实服务器发送请求 // 注意:这需要在onRequest里拦截并调用callback({ response: ... }),更常见的做法是在onRequest里拦截 // 但这里演示在onResponse里替换真实服务器的响应 const mockData = { code: 0, data: { userId: 1001, name: 'Mock User', avatar: 'https://placeholder.com/avatar.jpg' }, message: 'success (mocked)' }; responseInfo.statusCode = 200; responseInfo.headers['content-type'] = 'application/json; charset=utf-8'; responseInfo.body = Buffer.from(JSON.stringify(mockData), 'utf8'); // 删除可能存在的压缩头,因为我们提供了新的明文Body delete responseInfo.headers['content-encoding']; delete responseInfo.headers['content-length']; // 工具会重新计算 console.log(`已Mock接口: ${request.url}`); } // 场景2:修改真实服务器的响应(例如,给所有JSON响应加一个字段) if (statusCode === 200 && headers['content-type']?.includes('application/json') && body) { try { const jsonBody = JSON.parse(body.toString('utf8')); // 避免循环修改或破坏结构 if (jsonBody && typeof jsonBody === 'object' && !jsonBody._debug) { jsonBody._debug = { proxyModified: true, time: new Date().toISOString() }; responseInfo.body = Buffer.from(JSON.stringify(jsonBody), 'utf8'); // 同样需要清理压缩和长度头 delete responseInfo.headers['content-encoding']; delete responseInfo.headers['content-length']; } } catch (e) { /* 忽略解析错误 */ } } // 场景3:模拟服务器错误或延迟 if (request.url.includes('/api/unstable')) { // 模拟5秒延迟 await new Promise(resolve => setTimeout(resolve, 5000)); // 或者模拟500错误 // responseInfo.statusCode = 500; // responseInfo.body = Buffer.from('Internal Server Error (Simulated)', 'utf8'); } callback(); }, };4.3 场景三:请求阻断与重定向
// myRule.js - 阻断与重定向 module.exports = { onRequest: async (requestInfo, callback) => { const { url } = requestInfo; // 场景1:完全阻断对某个广告或统计域名的请求(提升页面加载速度) const blockList = ['ads.doubleclick.net', 'www.google-analytics.com', 'sdk.analytics.xxx']; if (blockList.some(domain => url.includes(domain))) { console.log(`阻断请求: ${url}`); // 直接返回一个空的成功响应,模拟请求被屏蔽 callback({ response: { statusCode: 200, headers: { 'content-type': 'text/plain' }, body: 'Blocked by proxy' } }); return; // 重要:直接返回,不再执行后续逻辑和转发 } // 场景2:将请求重定向到本地开发服务器 if (url.includes('production-api.example.com')) { const newUrl = url.replace('production-api.example.com', 'localhost:3000'); requestInfo.url = newUrl; console.log(`请求重定向至本地: ${newUrl}`); // 可能需要修改Host头,否则本地服务器可能无法识别虚拟主机 requestInfo.headers['host'] = 'localhost:3000'; } // 场景3:根据请求特征返回自定义响应(Mock) if (url.endsWith('/api/config') && requestInfo.method === 'GET') { callback({ response: { statusCode: 200, headers: { 'content-type': 'application/json' }, body: JSON.stringify({ theme: 'dark', language: 'zh-CN', mock: true }) } }); return; } // 如果不是上述情况,正常转发 callback(); } };5. 高级应用与性能调优
5.1 处理 WebSocket 连接
现代应用大量使用 WebSocket 进行实时通信。Shermie-proxy 通过配置websocket: true来支持代理 WebSocket 连接。在规则脚本中,你可以监听onWebSocketFrame等事件来拦截和修改 WebSocket 收发的数据帧。这对于调试聊天应用、实时仪表盘等场景非常有用。处理 WebSocket 时需注意,数据帧可能是文本也可能是二进制,需要根据opcode进行区分处理。
5.2 性能考量与优化建议
代理工具毕竟增加了一层网络跳转和处理,可能会对性能产生轻微影响,尤其是在记录完整请求/响应体时。以下是一些优化建议:
- 选择性记录:在规则脚本中,通过判断 URL、域名或内容类型,只记录你关心的请求。对于图片、视频、大型文件等请求,跳过
body的记录和打印。onResponse: async (responseInfo, callback) => { const contentType = responseInfo.headers['content-type'] || ''; if (contentType.startsWith('image/') || contentType.startsWith('video/') || contentType.includes('octet-stream')) { // 跳过媒体和二进制流体的详细记录 callback(); return; } // ... 其他处理逻辑 } - 使用静默模式:生产调试规则时,可以开启配置中的
silent: true,关闭控制台默认的请求日志,只输出你自己在规则脚本中定制的关键信息。 - 合理设置排除列表:将本地局域网地址、已知的大文件下载域名等加入
exclude配置,让它们直连,减轻代理负担。 - 避免同步阻塞操作:在
onRequest和onResponse钩子中,避免执行耗时的同步操作(如大型文件读写、复杂计算)。如果必须执行,确保使用异步方式,并尽快调用callback(),以免阻塞代理管道。
5.3 集成到自动化工作流
Shermie-proxy 的脚本化特性使其易于与自动化测试框架(如 Jest, Mocha, Playwright, Cypress)集成。你可以在测试套件启动前,以编程方式启动 Shermie-proxy 并加载特定的 Mock 规则,从而确保测试环境拥有稳定、可控的网络响应。
例如,使用child_process模块在 Node.js 测试脚本中启动代理:
const { spawn } = require('child_process'); const path = require('path'); beforeAll(async () => { // 启动代理服务器,指定配置文件 const proxyProcess = spawn('node', [path.resolve('./node_modules/.bin/shermie-proxy'), '-c', './testProxyConfig.js']); // 等待代理服务就绪 await new Promise(resolve => setTimeout(resolve, 2000)); global.__PROXY_PROCESS__ = proxyProcess; }); afterAll(() => { if (global.__PROXY_PROCESS__) { global.__PROXY_PROCESS__.kill(); } });这样,你的端到端(E2E)测试或接口测试就能在一个被精确控制网络行为的沙箱中运行。
6. 常见问题排查与实战技巧
6.1 HTTPS 抓包失败或证书错误
这是最常见的问题。
- 症状:浏览器访问 HTTPS 网站显示“您的连接不是私密连接”、“NET::ERR_CERT_AUTHORITY_INVALID”。
- 原因:客户端(浏览器/手机)没有安装或信任 Shermie-proxy 生成的根证书。
- 解决:
- 启动 Shermie-proxy(SSL 启用)后,访问
http://127.0.0.1:<端口>(如http://127.0.0.1:8899),页面通常会提供根证书下载链接。 - 下载
ca.crt文件。 - 导入系统或浏览器:
- macOS:双击
.crt文件,打开“钥匙串访问”,找到导入的证书,右键点击 -> “显示简介” -> “信任” -> 将“使用此证书时”设置为“始终信任”。 - Windows:双击
.crt文件 -> “安装证书” -> “当前用户” -> “将所有的证书都放入下列存储” -> “浏览” -> 选择“受信任的根证书颁发机构”。 - 浏览器:有些浏览器(如 Chrome)使用系统证书库,有些(如 Firefox)有独立的证书管理器,需要在浏览器设置中手动导入并信任。
- macOS:双击
- 手机端:确保通过
http://<电脑IP>:端口下载并安装了证书,且在系统设置中将其设置为“受信任的凭据”(位置因安卓版本而异)。
- 启动 Shermie-proxy(SSL 启用)后,访问
实操心得:有时候即使安装了证书,某些 App(尤其是安卓高版本或 iOS)仍可能抓不到包,这是因为它们使用了“证书锁定(Certificate Pinning)”技术。这种情况下,代理工具就无能为力了,除非能修改 App 本身。对于自己开发的 App,在开发阶段应关闭证书锁定。
6.2 代理设置后无法上网
- 症状:设置了代理,但浏览器无法访问任何网站,或只有部分网站无法访问。
- 排查:
- 检查代理服务是否运行:确认 Shermie-proxy 进程是否在运行,监听端口是否正确。
- 检查排除列表:是否错误地将需要访问的地址(如网关
192.168.1.1)加入了exclude列表?或者,是否应该将一些地址加入排除列表却没加? - 检查规则脚本:你的
onRequest回调函数是否在所有分支路径上都正确调用了callback()?如果某个条件分支下没有调用callback(),请求会被挂起。 - 检查防火墙:本地防火墙是否阻止了代理服务(Node.js 进程)访问网络?
6.3 请求体或响应体显示为乱码或空白
- 症状:抓到的请求/响应 Body 是乱码、
[object Object]或者直接是空白。 - 原因与解决:
- 压缩内容:服务器返回的响应可能经过 Gzip、Deflate 压缩。Shermie-proxy 默认可能会解压,但需确认配置。在规则脚本中,如果修改了响应体,需要删除
headers['content-encoding'],否则浏览器无法解压被修改过的压缩数据。 - 二进制内容:对于图片、PDF 等二进制数据,直接
toString()会乱码。在记录前应先判断content-type,对于二进制类型,可以记录为[Binary Data - size: xxx bytes]。 - Buffer 处理:
requestInfo.body和responseInfo.body通常是 Buffer 对象。需要使用body.toString('utf8')来转换为字符串(针对文本)。转换前最好用Buffer.isBuffer(body)判断一下。 - 大文件截断:为了性能和控制台可读性,你的规则脚本可能只打印了 Body 的前一部分字符。
- 压缩内容:服务器返回的响应可能经过 Gzip、Deflate 压缩。Shermie-proxy 默认可能会解压,但需确认配置。在规则脚本中,如果修改了响应体,需要删除
6.4 性能瓶颈与内存占用
- 症状:代理运行一段时间后变慢,或者 Node.js 进程内存占用越来越高。
- 优化:
- 关闭不必要的日志:使用
silent模式,并在规则脚本中避免对每个请求都执行console.log,尤其是打印完整的 Body。 - 限制 Body 记录大小:在配置中或规则脚本里,对于过大的请求/响应体,跳过记录或只记录元数据。
- 检查规则脚本内存泄漏:确保在规则脚本中没有意外地保存了对请求/响应对象的大型引用(例如将其推入一个全局数组且从不清理)。这会导致垃圾回收无法进行,内存持续增长。
- 定期重启:对于长期运行的调试任务,可以设置定时任务或手动定期重启代理服务。
- 关闭不必要的日志:使用
6.5 与其他工具冲突
- 症状:端口被占用,或者代理行为不符合预期。
- 解决:
- 端口占用:换一个端口,或关闭占用该端口的其他程序(如其他抓包工具、开发服务器)。
- 多层代理:如果你身处公司网络,已经有一层网络代理,那么你需要处理代理链。一种方法是在 Shermie-proxy 的规则中,对于需要访问外网的请求,将其转发到上层代理。这需要更复杂的网络配置。
- VPN 干扰:某些 VPN 软件会修改系统网络栈,可能与本地代理冲突。尝试暂时断开 VPN。
7. 安全使用须知与最佳实践
使用 MITM 代理工具 inherently 具有安全敏感性,请务必遵循以下准则:
- 仅用于合法调试:仅在你自己拥有完全控制权的设备、网络和应用程序上使用,用于开发、测试、调试目的。绝对不要用于拦截他人的网络通信,这是非法的。
- 保护你的根证书:生成的
ca.crt和ca.key.pem文件是安全关键资产。任何人拿到它们都可以对你信任该 CA 的网络流量进行解密。切勿将其提交到公开的代码仓库或通过网络传输。建议在.gitignore中忽略cert/目录。 - 及时移除证书:当结束调试后,应从系统和浏览器中移除你安装的自签名根证书,以免留下安全漏洞。
- 隔离使用环境:最好在虚拟机、容器或专用的开发机器上使用此类工具,与日常办公、金融操作的环境隔离开。
- 审查规则脚本:从网络上下载的规则脚本可能包含恶意代码。运行前务必仔细审查,尤其是涉及网络请求转发、数据上报等操作的代码。
我个人在长期使用这类工具的过程中,最大的体会是:明确边界,善用其长。Shermie-proxy 这类脚本化代理,其威力在于灵活性和自动化能力,适合深度、定制化的调试场景。对于简单的查看请求响应,图形化工具可能更直观;但对于需要集成到 CI/CD、自动化测试、或者复杂请求/响应修改链路的场景,它就是无可替代的利器。开始时可能会在证书配置、脚本编写上踩些坑,但一旦跑通,它将成为你开发工具箱里一件非常趁手的“手术刀”,能精准地解剖和调试网络层面的任何问题。最后一个小技巧:把你的常用规则脚本模块化,比如logRule.js、mockApiRule.js、blockAdsRule.js,然后在主配置中动态引入,这样就能像搭积木一样快速组合出当前调试任务所需的代理环境。
