前端开发最痛苦的瞬间是什么?不是样式调不出来,不是状态理不清,而是------后端说"接口明天给",然后你傻等了一天又一天。
好不容易等到接口好了,联调又是噩梦:字段对不上、跨域、数据结构变了......可以说,前后端耦合的等待成本,是拖慢项目进度的隐形杀手。
今天这篇实战文章,我会以一个经典的 Todo 全栈项目为例,手把手带你搭建一套前端接口工程,让你可以在后端接口就绪前,独立跑通整个应用。读完你会发现:原来前端开发可以这么爽。
一、先看清全栈项目的"前后端分离"到底是什么
很多同学对"前后端分离"的理解就是:你用 React,我用 Koa,咱们各写各的。实际上,分离远不止技术栈,更核心的是职责分离 和开发流程解耦。
在我们的 Todo 项目中:
- 前端三驾马车:React(组件) + React-Router(路由) + Zustand(状态管理),负责一切用户可见的界面交互。
- 后端:Node + Koa + MySQL,负责提供 RESTful API,返回 JSON 数据。
两者唯一的交集是什么?接口请求 。你会发现,如果前端直接死等后端提供 /api/todos 才能写渲染逻辑,那等于是把命脉交给了后端。真正的分离,应该让前端拥有独立完成应用的能力。
好的架构不是把东西分开写,而是让它们能独立跑起来。
二、前端独立开发的三驾马车:组件、路由、状态
先搭起我们前端项目的骨架。一个标准的 React SPA 离不开这三样:
- 组件:页面级组件(Pages)和通用组件(Components),UI 的砖瓦。
- 路由 :React-Router 接管页面级切换,比如
/到 Home,/todos到 Todos。 - 状态管理:Zustand 或 Hooks 管理响应式数据,相当于前端的"小银行"。
看看我们的路由配置(简版):
javascript
// App.jsx
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import Nav from './components/Nav';
const Todos = lazy(() => import('./pages/Todos'));
// ...
<Router>
<Nav />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/todos" element={<Todos />} />
</Routes>
</Router>
这时候页面已经能切换了,但是 Todos 页面一旦挂载就要展示数据,数据从哪里来?这就是前端工程化里最容易被忽略的一环------接口层。
三、打破耦合:前端为什么需要自己的"假后端"
先看一段典型的"傻等后端"的代码:
scss
// 在组件里直接 fetch 真实后端
useEffect(() => {
fetch('http://localhost:3000/api/todos')
.then(res => res.json())
.then(setTodos)
}, []);
这样做有两个致命问题:
- 后端没开发好,你连页面都无法渲染,完全阻塞。
- 即便后端好了,你可能会因为跨域、代理等问题折腾半天。
解决方案就是引入 Mock 机制 ------给前端自己造一个"假后端"。说白了就是:前端自己拦截请求,按约定的接口格式返回模拟数据 。我们这里用到的是 mockjs 库(配合 Vite 插件 vite-plugin-mock)。
这样,前端开发流程就变成了:
- 和后端约定好接口文档(URL、Method、响应结构)。
- 前端按文档编写 Mock 数据。
- 所有组件、状态、交互全部基于 Mock 接口开发,跑通整个 App。
- 后端交付后,一键切换真实接口,几乎零成本联调。
不要让接口成为阻塞你开发的理由,先造轮子再换轮子。
四、设计一个专业的前端接口层(api/ 目录)
我们不再在组件里直接写 fetch,而是统一管理所有请求。来看看项目中的 api 目录结构:
arduino
src/
api/
config.js // axios 实例,配置 baseURL、拦截器等
todos.js // 具体接口模块
1. 封装 axios 实例,关键就在 baseURL
javascript
// api/config.js
import axios from 'axios';
const instance = axios.create({
baseURL: '/api', // 开发阶段指向 Mock 服务器
timeout: 5000,
});
export default instance;
baseURL: '/api' 是精髓!因为我们的 Mock 会拦截所有 /api/* 的请求,而真实后端很可能也是 /api 前缀。将来只需把 baseURL 改成 'http://localhost:3000' 就能切换到真实接口,一处修改,全局生效。
2. 按业务模块拆接口
javascript
// api/todos.js
import axios from './config';
export const getTodos = async () => {
const res = await axios.get('/todos'); // 最终请求 /api/todos
return res.data; // 直接返回业务数据
};
这样组件里调用就非常干净:
ini
const data = await getTodos();
setTodos(data.todos);
3. Mock 数据长什么样?
我们用 vite-plugin-mock,在 mock/ 目录下创建文件,它会自动拦截匹配的请求:
yaml
// mock/todos.js
export default [
{
url: '/api/todos',
method: 'get',
timeout: 500, // 可选模拟网络延迟
response: () => {
return {
code: 0,
todos: [
{ id: 1, title: '学习前端接口工程', completed: true },
{ id: 2, title: '看龙餐馆', completed: false },
]
};
}
}
]
注意字段结构要和你跟后端约定的一致 ,比如我们统一用 code: 0 表示成功,数据放在 todos 里。这样前端解析逻辑就是固定的,未来切后端时完全不用改代码。
五、在 Vite 中集成 Mock 能力
安装依赖:
css
npm i mockjs vite-plugin-mock -D
在 vite.config.js 中引入插件:
javascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteMockServe } from 'vite-plugin-mock';
export default defineConfig({
plugins: [
react(),
viteMockServe({
mockPath: 'mock', // mock 文件所在目录
enable: true, // 开发环境开启
}),
],
});
重启项目后,访问 http://localhost:5173/api/todos(你前端的端口),你会发现返回的就是 Mock 数据!因为 Vite 服务器已经把请求代理到了 mock 处理逻辑中。
此时,你的前端 App 已经完全脱离后端运行了,而且对真实 API 的调用方式一模一样。
六、实战串联:Todo 页面独立跑起来
我们用 Hooks 状态管理(也可以用 Zustand)简单实现一下:
javascript
// pages/Todos.jsx (注:原笔记文件名有误,应为 Todos.jsx)
import { useState, useEffect } from 'react';
import { getTodos } from '../api/todos';
function Todos() {
const [todos, setTodos] = useState([]);
useEffect(() => {
(async () => {
const data = await getTodos();
setTodos(data.todos || []);
})();
}, []);
return (
<div>
<h2>Todos</h2>
<ul>
{todos.map(todo => (
<li key={todo.id} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.title}
</li>
))}
</ul>
</div>
);
}
这时候你打开 /todos 路由,完美展示两条 Mock 数据。整个流程和后端完全解耦。
七、联调切换,只需一行改动
等后端同事终于把接口调通了,你只需要做一件事:
打开 api/config.js,把 baseURL 改为真实后端地址:
arduino
baseURL: 'http://localhost:3000', // 指向真实 Koa 服务
然后确认后端的 /api/todos 返回结构包含 { code: 0, todos: [...] },OK,一切照旧,完美运行。Mock 到真实的切换,做到了零侵入。
如果你想更优雅,可以通过环境变量区分:
arduino
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
并在 .env.development 和 .env.production 中设置不同的地址,连手动改代码都省了。
八、总结与思考
通过这次 Todo 全栈实践,我们完成了前端工程化的一个重要拼图------前端接口层。它带来几个显著的收益:
- ✅ 前后端开发完全并行,不再互相等待。
- ✅ 前端自闭环,任意页面和交互都可提前验收。
- ✅ 联调时的切换成本几乎为零,减少扯皮。
- ✅ 统一的 axios 实例,方便后期加拦截器、错误处理等。
这套模式不仅适用于小型 Todo 项目,在复杂的企业级中台、BFF 层也同样有效。很多大厂的前端团队甚至会在本地 Mock 的基础上加入接口文档自动生成 Mock 数据的能力(如 Apifox、YApi),进一步提高效率。
最后分享一句我一直奉行的开发哲学: "不要让你的代码被任何外部依赖牵着鼻子走,能自己掌控的,就牢牢握在手里。"
希望这篇文章能帮你告别傻等后端的痛苦,下次再遇到"接口还没好"时,你可以淡定地说一句:"没关系,我先把整个 App 跑起来。"