如何在 connect 中注入 Mock Service Worker 拦截 API
解读
这道题表面问的是“把 MSW 塞进 connect”,实质考察三点:
- 你是否理解 connect 只是中间件栈,本身不生成 Service Worker 文件,也不负责运行时注入;
- 你是否知道 MSW 的浏览器端需要一段注册脚本+mockServiceWorker.js 文件,而这两个资源必须能被浏览器 200 拿到;
- 你是否能把 Grunt 的“静态托管”能力与 MSW 的“开发阶段拦截”能力拼成一条闭环,让“前端页面一刷新就自动注册、接口一调用就被拦截”,且 不改一行生产代码。
国内面试场景里,这道题常作为“本地联调没后端”的解决方案出现,答不出“静态文件怎么透传”会直接被判定为“只会 npm run,不会工程化”。
知识点
- connect:Node 生态最轻量的中间件内核,grunt-contrib-connect 基于它暴露静态托管、端口监听、livereload 等功能。
- MSW 双模式:
– browser 模式靠 Service Worker 拦截 fetch;
– node 模式靠拦截 http/https 模块,用于 Jest/Storybook。 - Service Worker 注册限制:
– 只能在 HTTPS 或 localhost 生效;
– 文件必须与页面同源且 通过网络请求 200 返回,不能是 dataURL 或 inline 脚本。 - grunt-contrib-connect 关键配置项:
– base:数组,决定静态根目录;
– middleware:函数,可插入任意 connect 中间件;
– livereload:布尔,决定是否注入 lr 脚本。 - MSW 初始化步骤:
- npx msw init <PUBLIC_DIR> --save;
- 在页面入口调用 worker.start();
- 保证 mockServiceWorker.js 落在 PUBLIC_DIR 下并能 200 访问。
答案
-
安装依赖
npm i -D msw grunt-contrib-connect -
把 MSW 的 worker 文件托管到 connect 能访问的位置
npx msw init static --save
这会在项目根生成 static/mockServiceWorker.js -
在 Gruntfile 里配置 connect,把 static 目录挂到根路径,并插入一段“兜底中间件”保证 *.js 返回正确 MIME:
grunt.initConfig({ connect: { options: { port: 8000, hostname: 'localhost', base: ['.', './static'], // 关键:static 目录被合并到根 middleware: function(connect, options, middlewares) { // 把 MSW 的 worker 文件优先级提到最前,防止被其他规则覆盖 middlewares.unshift(function(req, res, next) { if (req.url === '/mockServiceWorker.js') { res.setHeader('Content-Type', 'application/javascript; charset=utf-8'); return require('fs') .createReadStream(require('path').join(__dirname, 'static/mockServiceWorker.js')) .pipe(res); } next(); }); return middlewares; } } } }); -
在页面入口(例如 src/main.js)启动 MSW,仅开发环境生效:
if (process.env.NODE_ENV === 'development') { const { worker } = require('./mocks/browser'); worker.start({ onUnhandledRequest: 'bypass' }); } -
运行 Grunt 任务
grunt connect:keepalive
浏览器访问 http://localhost:8000,F12 → Application → Service Workers 可见 mockServiceWorker.js 已注册,接口调用被成功拦截。
要点回顾:
- static 目录必须出现在 base 数组里,否则 404;
- middleware 顺序决定优先级,把 worker 文件中间件放到最前;
- localhost 协议无证书限制,正是国内开发机最常见场景;
- mockServiceWorker.js 返回的 MIME 必须是 application/javascript,部分国产浏览器(360 兼容模式)对 text/plain 会拒绝注册。
拓展思考
- 如果团队把 Grunt 换成 Vite/Webpack5,思路完全一致:
把 mockServiceWorker.js 丢进 public 或 webpack static dir,保证构建 devServer 能 200 返回即可;MSW 与构建工具解耦,真正的门槛是“静态文件可达”而非“谁托管”。 - 在 HTTPS 内网环境(如 https://dev.company.com)使用 MSW,需要:
– 给 devServer 配置合法证书(公司内网 CA 签发);
– 或者让浏览器信任自签证书,否则 Service Worker 注册会被 DOMException: Failed to register a ServiceWorker: An SSL certificate error occurred 打断。 - 若想在 Grunt 驱动的单元测试(karma/jasmine)里用 MSW,必须切到 node 模式,因为 karma 启动的是无头浏览器,Service Worker 无法拦截本地 file:// 请求;此时用 server.listen() 即可,无需 connect 介入。
- 大型项目往往把 mock 数据按模块拆分,可以写一份 grunt-contrib-watch 任务:
当 mocks/*.js 变动时,热替换 MSW 的 handler 列表,无需重启 connect,实现“ mock 数据热更新”,这是国内中台项目面试的加分亮点。