React 前端如何不傻等后端接口?

前后端分离的口号大家都听过,但落到代码上,前端到底怎么"不依赖后端也能独立开发"?答案就藏在这七个文件里。本文用一个 Todos 项目,逐文件拆解前端是如何通过 API 层 + Mock 数据,把和后端的耦合降到最低的。


一句话概括分工:

文件 职责
main.jsx 启动应用
App.jsx 路由分配页面
Nav.jsx 导航链接
Home.jsx / Todos.jsx 页面内容
api/config.js + api/todos.js 数据请求层(前后端唯一的耦合点)

① main.jsx:入口,只做一件事

javascript 复制代码
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

逐行看:

  • createRoot(...):React 18+ 的新渲染入口,替代老的 ReactDOM.render
  • document.getElementById('root'):找到 index.html 里的 <div id="root">,整个应用渲染进这里。
  • <StrictMode>:开发模式的严格模式,帮你提前暴露一些潜在问题(比如副作用执行两次),生产环境自动失效。

入口文件很薄,这是好事------它只负责"启动",具体逻辑全交给 App


② App.jsx:路由接管一切(重点:懒加载)

javascript 复制代码
import React, { lazy, Suspense } from 'react';
import { Routes, Route, BrowserRouter as Router } from 'react-router-dom';
import Nav from './components/Nav';

const Home = lazy(() => import('./pages/Home'));
const Todos = lazy(() => import('./pages/Todos'));

function App() {
  return (
    <Router>
      <Nav />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/todos" element={<Todos />} />
      </Routes>
    </Router>
  )
}
export default App

1. <Router> 路由接管一切

  • <BrowserRouter>(别名 Router):用 HTML5 History API 管理地址栏 URL。
  • <Routes> + <Route>:根据当前 URL 匹配要渲染哪个组件。path="/" 渲染 Homepath="/todos" 渲染 Todos
  • <Nav /> 放在 <Routes> 外面,所以导航栏在所有页面都显示,只有下面区域随路由切换。

2. lazy + Suspense 按需加载(懒加载)

ini 复制代码
const Home = lazy(() => import('./pages/Home'));
  • lazy(() => import(...)):动态导入,切到某个页面时才去加载那个页面的代码,而不是一开始全部打包加载。
  • 这样首页首屏加载更快------用户可能根本不去 /todos,那就没必要一开始就下载它的代码。
  • 配套的 <Suspense> 用来在懒加载完成前显示占位内容(这里暂时没写 fallback,实际项目应补上,比如 <Suspense fallback={<div>加载中...</div>}>)。

小提醒:代码里 <Suspense> 其实没用到(lazy 组件没包进去),严格来说这是个待完善点。完整写法应该是:

xml 复制代码
<Suspense fallback={<div>加载中...</div>}>
  <Routes>...</Routes>
</Suspense>

javascript 复制代码
import { Link } from 'react-router-dom';

function Nav() {
  return (
    <nav style={{padding:10, borderBottom:'1px solid #ccc'}}>
      <Link to="/">Home</Link>
      <Link to="/todos">Todos</Link>
    </nav>
  )
}
export default Nav

关键点:为什么用 <Link> 而不是 <a>

  • <a href="/todos">刷新整个页面,重新加载所有资源------SPA(单页应用)的大忌。
  • <Link to="/todos"> 只改地址栏 + 触发路由切换,不刷新页面,体验流畅。

这是 React Router 最基础也最重要的一个习惯。


④⑤ Home.jsx / Todos.jsx:页面组件

Home.jsx 目前极简:

javascript 复制代码
function Home() {
  return (
    <>
      Home
    </>
  )
}
export default Home

Todos.jsx 是重点------它演示了页面怎么取数据

javascript 复制代码
import { getTodos } from '../api/todos';
import { useEffect, useState } from 'react';

function Todos() {
  const [todos, setTodos] = useState([]);

  useEffect(() => {
    (async () => {
      const data = await getTodos();
      setTodos(data);
    })()
  }, []);

  return (
    <>
      Todos
    </>
  )
}
export default Todos

逐行看:

  • useState([]):声明响应式状态 todos,初始为空数组。
  • useEffect(..., [])依赖数组为空,表示只在组件挂载时执行一次(等价于"页面加载时发一次请求")。
  • (async () => {...})()IIFE 立即执行函数 。因为 useEffect 的回调不能直接写成 async,所以包一层立即执行的 async 函数,才能在里面用 await
  • await getTodos():调接口拿数据。
  • setTodos(data):拿到数据后更新状态,触发重新渲染。

这里藏着"前后端分离"的雏形Todos.jsx 不关心数据从哪来、是真后端还是 Mock,它只管调用 getTodos() 这个接口函数。数据源怎么变,页面代码一行都不用改。


⑥ api/config.js:axios 实例化,baseURL 一键切换

javascript 复制代码
import axios from 'axios';

const instance = axios.create({
  baseURL: '/api',          // 前端模拟的请求地址 /api/todos
  // baseURL: 'http://localhost:3000',  // 切换真实后端时改这里
  timeout: 5000,
})

export default instance;

这是全项目最值钱的一个文件,逐行拆:

  • axios.create(...):创建一个配置好的 axios 实例 ,而不是到处 axios.get(...)
  • baseURL: '/api':所有请求自动加上 /api 前缀。所以 get('/todos') 实际请求的是 /api/todos
  • timeout: 5000:5 秒超时,避免请求卡死。
  • 注释里那行 baseURL: 'http://localhost:3000' 是关键:后端接口真正写好之后,只需要改这一行,把 /api 换成真实后端地址,全项目所有接口自动切换,不用去每个文件里一个个改。

为什么不用原生 fetch,要升级 axios?

fetch 功能太少:没有超时、没有拦截器、错误处理麻烦、要手动 .json()axios 提供了实例化、超时、拦截器等,是"从 fetch 升级到 axios"的标准姿势。


⑦ api/todos.js:接口统一管理

javascript 复制代码
import axios from './config';

export const getTodos = async () => {
  const res = await axios.get('/todos'); 
  return res.data;
}

这个文件体现了「一个模块一个 js 文件」的接口管理规范:

  • 所有和 todos 相关的接口,都放这一个文件里,导出给页面用。
  • 页面(Todos.jsx)只 import { getTodos } from '../api/todos'不直接接触 axios、不接触 URL
  • 好处:将来接口变化、要加请求头、要统一错误处理,都只需改 api 目录,页面无感知。

这里有个小细节:axios.get('/todos') 拼上 config.js 里的 baseURL: '/api',最终请求 /api/todos。这个 /api/todos 正是 Mock 拦截的地址,前后端在这里"接上头"。


附:mock/todos.js ------ 让前端"不傻等后端"的关键

yaml 复制代码
export default [
  {
    url:'/api/todos',
    method: 'get',
    timeout: 2000,
    response: (req, res) => {
      return {
        code: 0,
        todos: [
          { id: 1, title: '学习前端接口工程', completed: true },
          { id: 2, title: '看龙餐馆', completed: false }
        ]
      }
    }
  }
]

配合 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 文件所在目录
    localEnabled: true     // 开发环境开启 mock
  })],
})

它的作用:

vite-plugin-mock 在开发服务器里拦截 /api/todos 这个请求,直接返回 mock/todos.js 里写死的假数据,根本不走后端

于是前端在开发阶段就有了自己的"假接口",可以先独立把界面写完、把数据流跑通 。等后端真正把 /api/todos 写好,再把 config.jsbaseURL 一切换,无缝对接。


串起来:一次请求的完整链路

  1. 用户访问 /todosApp.jsx 路由匹配到 <Todos />
  2. Todos.jsx 挂载 → useEffect 触发 getTodos()
  3. api/todos.jsconfig.js 配置好的 axios 实例,请求 baseURL + '/todos' = /api/todos
  4. 开发阶段 vite-plugin-mock 拦截 /api/todos,返回 mock/todos.js 的假数据。
  5. 数据回到 Todos.jsxsetTodos 触发渲染。

整条链路里,页面(Todos.jsx)从头到尾不知道数据是"假"的------这正是前后端分离要达到的效果。


总结:这七个文件教会我们的三件事

核心思想 落地点
解耦的关键 = 接口请求 前端只认 getTodos(),不认后端
api 目录统一管理接口 config.js 管配置,todos.js 管接口
baseURL 一键切换 Mock → 真实后端只改一行

再加一句工程方法论:前端永远不要傻等后端接口 。先建好 API 层 + Mock 数据,把界面和交互完整做出来;后端接口好了之后,切一行 baseURL 就能连调。这就是"前后端分离"在代码层面的真正含义。

相关推荐
xiaominlaopodaren1 小时前
three.js地图数学基础(七):地图相机
javascript·gis·three.js
岁月留痕1681 小时前
6 Flutter 篇:Flutter 分层式架构设计
前端
乘风gg1 小时前
AI Coding:从单兵提效到多 Agent 的团队全链路协作模式
前端·ai编程·claude
万维易源1 小时前
免费药品信息查询:用API 读懂常用药
java·前端·数据库·药品信息·药品查询·药品查询api
星火10241 小时前
【LangChain4j系列03】AI Services 高层抽象设计解析
人工智能·后端
勾勾圈圈蛋蛋1 小时前
Vue2 与 Vue3 响应式数据原理详解
前端
星火10241 小时前
【LangChain4j系列04】Tools 工具调用机制详解
人工智能·后端
用户69371750013841 小时前
深夜炸场!DeepSeek 没发新模型,却重构了整个 Agent 生态
前端·后端·github
岁月留痕1681 小时前
16 实战项目一:实现暗黑模式
前端