稀土掘金技术社区

【前端效率工具】再也不用 APIfox 联调!零侵入 Mock,全程不改代码、不开代理

图片(💰TRAE SOLO实战赛强势上线,速戳上图了解详情🔍)

❝

上周四下午,我正在调样式,突然电脑风扇开始咆哮。

打开任务管理器:「内存占用 15.5G / 16G」。

我人傻了,16G 内存开发个前端项目还能卡?

仔细一看:

  • 「VSCode」:2.1G(跑着项目,正常)
  • 「Chrome 30 个标签页」:4.5G(掘金、Stack Overflow、GitHub、文档...能理解)
  • 「APIfox」:1.5G(???)
  • 「微信 + 企业微信」:1.1G(没办法,工作要用)
  • 「其他若干进程…」:...

「APIfox这一个工具,占了 1.5G?」

关键是,我只是想 Mock 几个接口而已,为什么要:

  • 装一个 500M+ 的客户端
  • 开代理(还得记得关)
  • 来回切窗口(浏览器 → APIfox → VSCode)
  • 占用 1.5G 内存

我试过其他方案:

  • 「在代码里写死 Mock 数据」 → 有时候忘记删掉,上线差点带着假数据冲进生产
  • 「用 Mock.js」 → 配置太麻烦,还要改代码

我想要的是:不装客户端、不改代码、不开代理、不耗内存、一键切换 Mock 开关 。 于是,我花了一个周末,做了一个chrome浏览器插件。

「现在mock不用 APIfox,内存占用降了不少,电脑顺多了,浏览器里一键 Mock,调试又快又省心。」

想知道怎么实现的吗?接下来将从零开始,和你一起实现这个mock小工具

❞

效果演示:  下面是插件在拦截 fetch/ajax 请求并返回 Mock 数据时的演示:

Image

插件功能预览

  • 拦截页面的 fetch、ajax 请求
  • 规则支持模糊匹配和完全匹配
  • 支持按 HTTP 方法过滤(GET/POST/PUT/DELETE)
  • 自定义返回 JSON 数据
  • 持续优化中:性能、匹配规则和用户体验会不断迭代,欢迎反馈与建议

根本原理:劫持浏览器的网络请求方法

一句话总结

「在网页加载前,偷偷替换掉浏览器原生的」「fetch」「和」「XMLHttpRequest」「,让所有网络请求先经过我们的"检查站",符合规则的就返回假数据,不符合的就放行。」

原理:

正常情况:网页代码 → fetch('/api/user') → 浏览器发送真实请求 → 服务器
插件介入后:网页代码 → fetch('/api/user')            ↓       我们的假 fetch(检查是否需要 Mock)           ↓    需要 Mock?    ├─ 是 → 直接返回假数据 ✅    └─ 否 → 调用真正的 fetch 发送请求 → 服务器

具体实现(简化版)

// 1. 保存原始方法const 真正的fetch = window.fetch;
// 2. 替换成我们的方法window.fetch = function(url) {  // 3. 检查是否需要 Mock  if (url.includes('/api/user')) {    console.log('拦截成功!返回假数据');    return Promise.resolve({      json: () => ({ name: 'Mock User' })    });  }
  // 4. 不需要 Mock,调用原始方法  return 真正的fetch(url);};

「关键点:」

  • 必须先保存原始方法,否则无法发送真实请求
  • 必须在网页加载前执行,否则拦截不到早期请求
  • XMLHttpRequest 同理,重写 open 和 send 方法

项目结构

quick-mock/├── manifest.json      # 插件配置文件├── popup.html         # 弹窗页面├── popup.css          # 弹窗样式├── popup.js           # 弹窗逻辑├── content.js         # 内容脚本 └── injected.js        # 注入脚本 

🚀 快速开始

获取代码

  • 「GitHub」:https://github.com/Teernage/quick-mock
    ⭐如果对您有帮助欢迎Star
  • 「Gitee」:https://gitee.com/xuzhenxin110/quick-mock

一键安装

  1. 打开Chrome浏览器
  2. 地址栏输入:chrome://extensions/
  3. 打开右上角"开发者模式"
  4. 点击"加载已解压的扩展程序"
  5. 选择quick-mock文件夹
  6. 搞定!扩展安装完成!

深入解析:从零开始理解插件实现

界面层(用户交互)

1. 「popup.html」 - 插件的"脸面"-   作用:用户点击插件图标看到的弹窗界面

  • 包含:输入框、按钮、规则列表
❝

🔗 「文件源码链接」:

  • GitHub: https://github.com/Teernage/quick-mock/blob/main/popup.html
  • Gitee: https://gitee.com/xuzhenxin110/quick-mock/blob/main/popup.html
❞

2. 「popup.css」 - 界面样式

❝

🔗 「文件源码链接」:

  • GitHub: https://github.com/Teernage/quick-mock/blob/main/popup.css
  • Gitee: https://gitee.com/xuzhenxin110/quick-mock/blob/main/popup.css
❞

3. 「popup.js」 - 界面逻辑

作用:处理用户配置mock操作

功能:

  • 点击"添加规则" → 保存到 chrome.storage
  • 点击"删除" → 从 chrome.storage 移除
  • 渲染规则列表
❝

🔗 「文件源码链接」:

  • GitHub: https://github.com/Teernage/quick-mock/blob/main/popup.js
  • Gitee: https://gitee.com/xuzhenxin110/quick-mock/blob/main/popup.js
❞

核心层(拦截逻辑)

4. 「content.js」 - "中间人"

作用:连接插件和网页的桥梁

能力:

  • 可以访问 Chrome API(读取 chrome.storage)
  • 可以访问网页 DOM
  • 不能访问网页的 JavaScript 环境(window.fetch)

职责:

  1. 把 injected.js 注入到网页
  2. 读取用户配置的 Mock 规则
  3. 通过 postMessage 与 injected.js 通信
❝

🔗 「文件源码链接」:

  • GitHub: https://github.com/Teernage/quick-mock/blob/main/content.js
  • Gitee: https://gitee.com/xuzhenxin110/quick-mock/blob/main/content.js
❞

5. 「injected.js」 - "劫匪"

作用:真正执行拦截的代码

能力:

  • 可以访问网页的 JavaScript 环境(window.fetch)

  • 可以重写 fetch 和 XMLHttpRequest

  • 不能访问 Chrome API(chrome.storage)

职责:

  1. 重写 window.fetch
  2. 重写 XMLHttpRequest.prototype.open/send
  3. 拦截请求,询问 content.js 是否需要 Mock
  4. 根据回复决定返回假数据还是真实请求
❝

🔗 「文件源码链接」:

  • GitHub: https://github.com/Teernage/quick-mock/blob/main/injected.js
  • Gitee:https://gitee.com/xuzhenxin110/quick-mock/blob/main/injected.js
❞

💡 深入理解脚本通信机制

如果你想深入了解 background/service worker、content script、injected script、popup 之间真实的运行上下文与消息链路,强烈推荐阅读: 「〈大部分人都错了!这才是 Chrome 插件多脚本通信的正确姿势〉」 这篇文章用原理+示例拆解了常见误区与正确做法,能帮你更透彻地理解本插件的通信机制。

配置层

6. 「manifest.json」 - 插件的"身份证"

❝

🔗 「文件源码链接」:

  • GitHub: https://github.com/Teernage/quick-mock/blob/main/manifest.json
  • Gitee:https://gitee.com/xuzhenxin110/quick-mock/blob/main/manifest.json
❞

安装扩展

  1. 打开Chrome浏览器
  2. 地址栏输入:chrome://extensions/
  3. 打开右上角"开发者模式"
  4. 点击"加载已解压的扩展程序"
  5. 选择quick-mock文件夹
  6. 搞定!扩展安装完成!
Image

文件协作流程(完整链路)

第一阶段:用户配置规则

┌─────────────┐│  用户操作    │ 在 popup.html 输入 Mock 规则└──────┬──────┘       │       ↓┌─────────────┐│  popup.js   │ 点击"添加"按钮└──────┬──────┘       │       │ chrome.storage.local.set({ mockRules: [...] })       ↓┌─────────────┐│Chrome Storage│ 持久化存储规则(关闭浏览器也不丢失)└─────────────┘

第二阶段:页面加载时注入脚本

用户打开网页(如 https://example.com)       ↓┌─────────────┐│manifest.json│ 检测到匹配的 URL└──────┬──────┘       │       │ 自动注入       ↓┌─────────────┐│ content.js  │ 在网页上下文运行(但在隔离沙箱)└──────┬──────┘       │       │ 创建 <script> 标签       │ script.src = chrome.runtime.getURL('injected.js')       │ document.head.appendChild(script)       ↓┌─────────────┐│ injected.js │ 在网页真实环境运行└──────┬──────┘       │       │ window.fetch = 我们的假fetch;       ↓    拦截就绪!

第三阶段:拦截请求(实时通信)

网页代码执行:fetch('/api/user')       ↓┌─────────────────────┐│ injected.js         │ 我们的假 fetch 被调用 └──────┬──────────────┘       │       │ window.postMessage({       │   type: 'MOCK_REQUEST',       │   url: '/api/user',       │   method: 'GET'       │ })       ↓┌─────────────────────┐│ content.js          │ 监听 message 事件└──────┬──────────────┘       │       │ 1. 读取 chrome.storage.local       │ 2. 遍历规则,检查是否匹配       │ 3. 找到匹配规则       ↓       │ window.postMessage({       │   type: 'MOCK_RESPONSE',       │   shouldMock: true,       │   mockData: { name: 'Mock User' }       │ })       ↓┌─────────────────────┐│ injected.js         │ 收到回复└──────┬──────────────┘       │       │ if(shouldMock) {       │   return new Response(JSON.stringify(mockData));       │ } else {       │   return 真fetch(url); // 调用原始方法       │ }       ↓网页代码收到响应:{ name: 'Mock User' }

为什么要分 content.js 和 injected.js?

只用 Content Script 行不行?

「不行!」 因为 Content Script 运行在隔离的沙箱中,无法访问网页的 window.fetch。

// content.js 中这样做是无效的!window.fetch = function() {   console.log('拦截失败!'); // 网页看不到这个修改}

只用 Injected Script 行不行?

「不行!」 因为 Injected Script 无法访问 chrome.storage 等 Chrome API,无法读取用户配置的 Mock 规则。

### 正确方案:两者配合
用户配置 Mock 规则↓存储到 chrome.storage (Popup)↓读取规则 (Content Script) ← 可以访问 Chrome API↓通过 postMessage 通信↓拦截 fetch (Injected Script) ← 可以修改 window.fetch

通俗比喻

content.js = 银行金库管理员  - 有钥匙(Chrome API 权限)  - 能读取保险箱(chrome.storage)  - 但不能直接接触客户(网页 JavaScript)
injected.js = 银行大堂经理  - 直接面对客户(网页代码)  - 能拦截客户请求(重写 fetch)  - 但没有金库钥匙(无法访问 chrome.storage)
解决方案:两人用对讲机(postMessage)通信  客户发起请求 → 大堂经理拦截 → 对讲机问管理员"要不要放行"  → 管理员查保险箱 → 回复"不放行,给假钞" → 大堂经理返回假钞

关键技术点总结

1. 「为什么要用」「run_at: "document_start"」「?」

// manifest.json"run_at": "document_start"  // 在 HTML 解析前运行

「原因:」 如果网页在插件加载前就执行了 fetch('/api/data'),我们就拦截不到了。

2. 「为什么要用」「web_accessible_resources」「?」

// manifest.json"web_accessible_resources": [{  "resources": ["injected.js"],  "matches": ["<all_urls>"]}]

「原因:」 默认情况下,网页无法加载插件内部的文件(跨域限制)。这个配置相当于给 injected.js 开了"绿色通道"。

3. 「为什么用」「postMessage」「而不是全局变量?」

// ❌ 错误做法window.mockRules = [...];  // content.js 设置console.log(window.mockRules);  // injected.js 读取(读不到!)
// ✅ 正确做法window.postMessage({ type: 'MOCK_REQUEST' }, '*');  // injected.js 发送window.addEventListener('message', (e) => { ... });  // content.js 接收

「原因:」 content.js 和 injected.js 虽然在同一个网页,但 JavaScript 环境是隔离的,就像两个平行世界,只能通过 postMessage 这个"传送门"通信。

4. 「为什么要保存原始 fetch、xhr?」

const originalFetch = window.fetch;  //  必须先保存
window.fetch = async function(url) {  if (needMock) {    return mockResponse;  }  return originalFetch(url);  //  不 Mock 时调用原始方法};

「原因:」 如果不保存,所有请求都会被拦截,无法发送真实请求。

总结

核心要点

  1. 「根本原理」:重写 window.fetch 和 XMLHttpRequest,在网页代码执行前劫持请求
  2. 「为什么分两个脚本」:content.js 能读插件配置,injected.js 能拦截请求
  3. 「怎么通信」:postMessage(唯一方式)
  4. 「什么时候注入」:document_start(越早越好)
  5. 「为什么能拦截」:在网页代码执行前就替换了原生方法

适用场景

  • 前端开发时,后端接口还没好
  • 调试线上 Bug,想临时改返回数据
  • 演示 Demo,不想依赖真实服务器
  • 自动化测试,需要稳定的 Mock 数据
  • 接口文档不完善,想自己造数据测试

最后

如果这个插件帮到了你,欢迎:

  • 如果觉得对您有帮助,欢迎点赞 👍 收藏 ⭐ 关注 🔔 支持一下!
  • ⭐ 「GitHub Star 支持」https://github.com/Teernage/quick-mock
  • 💬 「评论区聊聊你的使用场景」

这个插件会持续优化,支持更丰富的配置项、更好的交互等

图片
点击"阅读原文"了解详情~