前端开发别拿生产 Key 刷数据:用 MSW 给搜索接口做 Mock

做搜索数据工具的前端时,我一开始的流程很粗糙:本地开发把 fetch 指向真实接口,用开发环境的 Key 直接调。结果三个问题接踵而至------调试一个加载状态刷新十次页面就是十次请求;下班后想改样式,网络一抖就全报错;更别提 Key 在本地散落,随时可能被提交进仓库。后来用 MSW(Mock Service Worker)把接口 Mock 掉,开发体验才算正常。这篇记录整套做法。

为什么是「网络层 Mock」而不是改代码

一些人的做法是在代码里加开关:if (isDev) return fakeData。这有两个坏处:业务代码被污染,且真实请求路径在开发时根本没被走到。

MSW 的思路不同------它用 Service Worker 在浏览器网络层拦截请求 。你的 fetch 代码一行不改,请求发出去就被接管,返回你定义的 mock 数据。切到真实接口时,把 mock 关掉即可,代码路径完全一致。

第一步:采集一次真实响应做 fixture

Mock 数据不能凭空编------用真实响应做样本,字段结构才保真。用一个脚本各调一次需要的端点,把响应存下来:

python 复制代码
import json, os, requests

FIXTURES = [
    {"endpoint": "search", "params": {"q": "搜索 api", "hl": "zh-cn", "gl": "cn"}},
    {"endpoint": "search", "params": {"q": "python tutorial", "hl": "en", "gl": "us"}},
]

for f in FIXTURES:
    resp = requests.post(
        f"https://api.serpbase.dev/google/{f['endpoint']}",
        headers={"Content-Type": "application/json", "X-API-Key": os.environ["SERPBASE_API_KEY"]},
        json=f["params"], timeout=30,
    )
    name = f"{f['endpoint']}-{f['params']['q'].replace(' ', '-')}.json"
    json.dump(resp.json(), open(f"mocks/fixtures/{name}", "w", encoding="utf-8"), ensure_ascii=False)
    print("saved", name)

注意采集「不完美」的样本 :至少存两份------一份字段齐全的,一份 snippet 缺失或没有精选摘要的。理由后面讲。

第二步:写 handlers

ts 复制代码
// mocks/handlers.ts
import { http, HttpResponse, delay } from "msw";
import searchCn from "./fixtures/search-搜索-api.json";
import searchEn from "./fixtures/search-python-tutorial.json";

const fixtures: Record<string, unknown> = {
  "搜索 api": searchCn,
  "python tutorial": searchEn,
};

export const handlers = [
  http.post("https://api.serpbase.dev/google/search", async ({ request }) => {
    const body = (await request.json()) as { q?: string };

    await delay(400); // 模拟真实延迟:让骨架屏/加载态真的有机会出现

    // 场景:接口错误(业务错误信封)
    if (body.q === "错误测试") {
      return HttpResponse.json({ status: 1, request_id: "mock_err", error: "upstream_timeout" });
    }

    // 场景:命中 fixture
    if (body.q && fixtures[body.q]) {
      return HttpResponse.json(fixtures[body.q]);
    }

    // 场景:空结果 / 字段缺失样本
    return HttpResponse.json({
      status: 0,
      request_id: "mock_req_001",
      elapsed_ms: 420,
      credits_charged: 0, // mock 不扣费
      search_type: "search",
      query: body.q,
      page: 1,
      organic: [
        { rank: 1, title: "无摘要样本", link: "https://example.com/1" }, // 故意没有 snippet
      ],
    });
  }),
];

三个 mock 场景是刻意为之:错误信封 测错误处理、缺字段样本 测 .get() 兜底、没有 fixture 的查询走默认空结果。

第三步:启动与开关

ts 复制代码
// app 入口(仅开发环境)
if (process.env.NODE_ENV === "development" && process.env.NEXT_PUBLIC_API_MOCK === "1") {
  const { worker } = await import("./mocks/browser");
  await worker.start({ onUnhandledRequest: "bypass" }); // 未 mock 的请求放行
}
ts 复制代码
// mocks/browser.ts
import { setupWorker } from "msw/browser";
import { handlers } from "./handlers";
export const worker = setupWorker(...handlers);

.env.development 里设 NEXT_PUBLIC_API_MOCK=1;需要联调真实接口时改成 0 即可,回归成本几乎为零。

踩坑记录

坑 1:fixture 全是「理想响应」。 样本里字段全有,开发时一切正常,上线遇到 snippet 缺失直接崩。fixture 里必须混着缺字段的样本。

坑 2:没模拟延迟。 Mock 瞬间返回,加载态永远闪不出来,等真接口上来才发现骨架屏布局歪了。delay(400) 一行解决。

坑 3:Mock 进了生产包。 worker.start() 没加环境判断,线上也拦截请求。用 NODE_ENV === "development" + 环境变量双重判断。

坑 4:fixture 半年不更新。 真实接口加了新模块,fixture 还是老样子,开发永远看不到新字段。给 fixture 加「保质期」:每个季度重跑一次采集脚本。

坑 5:从不碰真实接口。 全 Mock 开发久了,不知道真实响应长啥样。建议留一个「冒烟脚本」,发布前用真实 Key 跑一次关键路径(几次请求而已,免费额度足够)。

工程清单

  1. 采集真实响应做 fixture,必须包含缺字段样本
  2. MSW 网络层拦截,业务代码零污染
  3. handler 覆盖:正常/错误信封/空结果/延迟四类场景
  4. NODE_ENV + 环境变量双重开关,杜绝进生产
  5. fixture 定期重录;发布前留一次真实接口冒烟

Mock 的本质不是「造假数据」,而是把「接口的不确定性」从日常开发里拿掉:加载态、错误态、缺字段态都能随时复现,请求不花钱、不吃限流、离线也能写代码。开发速度的提升,比省下的那点 credits 值钱得多。

采集 fixture 时每个端点一次调用,注册送的 100 次免费搜索足够覆盖;接口的错误信封和字段结构见 SerpBase 官方文档,照着写 handler 不会跑偏。你们前端联调是直连真实接口还是 Mock?评论区聊聊。

相关推荐
光影少年1 小时前
RN启动流程(bundle加载→Bridge初始化→首屏渲染)
前端·react native·react.js
超人气王1 小时前
Agent 提示词工程:从「写 Prompt」到「设计行为控制系统」
前端·前端框架
恋猫de小郭1 小时前
Flutter Golden Tests:给 AI Agent 的 UI 测试系统
android·前端·flutter
特级业务专家1 小时前
X6 框选拖拽性能内幕:从 issue 4823 到开源内核(系列 3 篇)之二
前端
特级业务专家1 小时前
X6 框选拖拽性能内幕:从 issue 4823 到开源内核(系列 3 篇)之三
前端
OpenTiny社区1 小时前
码道结合 OpenTiny 智能化 Skill,让页面会“听话”!
前端·人工智能·开源
kyriewen1 小时前
我手写了一版 React Compiler:AI 最常漏的 3 个 memo 场景
前端·javascript·ai编程
IT_陈寒1 小时前
Python的列表推导式差点让我加班到凌晨
前端·人工智能·后端