mock 数据可直接切换,前提是外部调用(如 fetch、axios.get)通过参数注入或统一容器获取,避免硬编码;推荐用工厂函数集中创建服务实例,并通过 services 对象注入,而非 provide('api', new apiclient())。

能直接切换 Mock 数据,前提是依赖不写死在组件或 Hook 里——只要所有外部调用(如 fetch、axios.get、new ApiClient())都通过参数传入或从统一容器获取,就能做到零源码修改切换 Mock。
把真实请求逻辑抽成可替换的函数参数
最轻量、见效最快的改造方式:把原本硬编码的请求调用,改成由外层注入。不是“封装一层 API 工具”,而是让业务逻辑彻底不感知具体实现。
- 错误写法:
useEffect(() => { fetch('/api/user') })——fetch是全局绑定,无法拦截或替换 - 正确写法:
const useUser = (requestFn = fetch) => { useEffect(() => { requestFn('/api/user') }) } - 生产环境调用:
useUser()(默认用原生fetch) - 测试或 Mock 环境调用:
useUser(mockFetch)(传入 Jest Mock 函数) - 注意:若使用
axios或自定义 client,同样适用 ——useUser(axios.get)或useUser(apiClient.getUser)
用工厂函数统一创建服务实例,避免 new 关键字散落各处
new ApiClient() 出现在多个文件里,等于把实现耦合进业务线;一旦要加日志拦截、换 base URL、切 Mock 实例,就得全局搜索替换 —— 这不是解耦,是批量改字符串。
前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。
- 新建一个
services.ts,集中导出工厂函数:export const createApiClient = (options: { baseUrl: string }) => new ApiClient({ baseUrl: options.baseUrl }); - 在入口或顶层组件中一次性创建并注入:
const services = { api: createApiClient({ baseUrl: import.meta.env.VITE_API_URL }), logger: new ConsoleLogger(import.meta.env.DEV) }; - 后续所有地方都从
services对象取实例,而不是自己new - Mock 场景下,只需替换
createApiClient返回值为mockApiClient,其余代码完全不动
Vue provide/inject 不是 DI 容器,别把它当注册中心用
provide('api', new ApiClient()) 看似解耦,实则埋雷:实例在 provide 时就已创建,无法按需延迟初始化,也无法跨模块复用同一实例,更难做类型安全校验。
- ✅ 正确用法:
provide('services', services)—— 提供的是已构造好的稳定对象 - ❌ 错误用法:
provide('api', new ApiClient())—— 每次 provide 都新建,且无法控制生命周期 - TypeScript 下必须用
InjectionKey显式声明类型,否则inject('services')返回any,IDE 补全失效、重构易出错 - 如果项目已有复杂服务依赖图(比如 A 依赖 B,B 依赖 C),
provide/inject很快会变成隐式依赖链,建议直接上inversify管理契约
Mock 切换的关键不在“怎么 mock”,而在“谁控制创建时机”
很多人卡在“Jest 怎么 mock axios”,其实问题根源是:mock 的目标不是库,而是你代码里真正调用的那个函数引用。只要那个引用可控,mock 就只是替换一个变量的事。
- 不要 mock 全局
axios模块(容易污染其他测试),而应 mock 你实际传入的apiClient.getUser方法 - 构建环境变量开关:
import.meta.env.MODE === 'mock',在入口判断后决定用哪个工厂函数 - Mock 实现本身可以非常简单:
{ getUser: () => Promise.resolve({ id: 1, name: 'test' }) },无需启动 server 或代理 - 真正复杂的点在于:服务之间有依赖(比如
logger被api内部调用),这时必须保证注入顺序和单例一致性 —— 这正是轻量 DI 容器(如inversify)比手写工厂更可靠的地方
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










