从 Vite 到 Axios 与 Mock:React Todos 全栈项目架构及请求链路详解

React Todos 全栈项目入门:Vite、路由、Axios 与 Mock 请求链路

  • 前言
  • [1. Axios 基础知识](#1. Axios 基础知识)
    • [1.1 Axios 是干什么的](#1.1 Axios 是干什么的)
    • [1.2 Axios 实例是什么](#1.2 Axios 实例是什么)
    • [1.3 Axios 常用配置参数](#1.3 Axios 常用配置参数)
      • [1.3.1 baseURL 公共地址前缀](#1.3.1 baseURL 公共地址前缀)
      • [1.3.2 timeout 超时时间](#1.3.2 timeout 超时时间)
      • [1.3.3 headers 请求头](#1.3.3 headers 请求头)
      • [1.3.4 params 查询参数](#1.3.4 params 查询参数)
      • [1.3.5 Cookie 与 withCredentials](#1.3.5 Cookie 与 withCredentials)
  • [2. Vite 项目架构与依赖安装](#2. Vite 项目架构与依赖安装)
    • [2.1 Vite 在项目中起什么作用](#2.1 Vite 在项目中起什么作用)
    • [2.2 路由、Axios 与 Mock 依赖](#2.2 路由、Axios 与 Mock 依赖)
  • [3. 项目目录与各模块职责](#3. 项目目录与各模块职责)
    • [3.1 项目结构](#3.1 项目结构)
    • [3.2 index.html 与 main.jsx:应用如何启动](#3.2 index.html 与 main.jsx:应用如何启动)
  • [4. React Router 页面路由](#4. React Router 页面路由)
    • [4.1 App.jsx 路由中心](#4.1 App.jsx 路由中心)
    • [4.2 Nav 和 Route 为什么都需要](#4.2 Nav 和 Route 为什么都需要)
  • [5. 项目中的 Axios API 层](#5. 项目中的 Axios API 层)
    • [5.1 config.js:统一配置怎么请求](#5.1 config.js:统一配置怎么请求)
    • [5.2 api/todos.js:定义请求什么数据](#5.2 api/todos.js:定义请求什么数据)
  • [6. Vite Mock 模拟接口](#6. Vite Mock 模拟接口)
    • [6.1 vite.config.js 注册 Mock 插件](#6.1 vite.config.js 注册 Mock 插件)
    • [6.2 mock/todos.js 返回模拟数据](#6.2 mock/todos.js 返回模拟数据)
  • [7. Todo.jsx 与完整数据链路](#7. Todo.jsx 与完整数据链路)
    • [7.1 Todo.jsx 进入页面后做什么](#7.1 Todo.jsx 进入页面后做什么)
    • [7.2 data 到底来自哪里](#7.2 data 到底来自哪里)
    • [7.3 请求消息如何走完整个项目](#7.3 请求消息如何走完整个项目)
  • [8. 当前全栈项目处于什么阶段](#8. 当前全栈项目处于什么阶段)
  • 总结

前言

一个基础的 Todos 项目,已经包含了前端工程中非常重要的几部分:Vite 负责启动开发环境,React 负责渲染页面,React Router 负责切换页面,Axios 负责发送 HTTP 请求,Vite Mock 则在后端尚未完成时提供模拟数据。

理解这个项目的关键,不是单独记住某一行语法,而是弄清楚各个模块分别负责什么,以及它们如何连接起来:

text 复制代码
进入 /todos 页面
→ Todo 组件开始执行
→ 调用 getTodos()
→ Axios 发送 GET /api/todos
→ Vite Mock 匹配这个请求
→ Mock 返回 Todo 数据
→ Axios 接收响应
→ Todo 组件拿到 data

本文先讲 Axios 的基础知识,再介绍 Vite 项目架构、目录组成、页面路由、API 层和 Mock 层,最后把整条请求过程串起来。

1. Axios 基础知识

1.1 Axios 是干什么的

Axios 是一个 HTTP 请求库,用来帮助前端向服务器发送请求,并接收服务器返回的数据。

Axios 运行在浏览器中的 JavaScript 代码里。真正提供网络能力的是浏览器,Axios 在浏览器网络能力的基础上封装了一套更方便的调用方式。

例如,前端需要获取 Todo 数据,可以发送一个 GET 请求:

js 复制代码
axios.get('/api/todos')

前端需要提交一条新任务,可以发送一个 POST 请求:

js 复制代码
axios.post('/api/todos', {
  title: '学习 Axios',
  completed: false,
})

常见 HTTP 请求可以这样理解:

Axios 调用 HTTP 方法 常见作用
axios.get() GET 查询数据
axios.post() POST 新增数据
axios.put() PUT 修改完整数据
axios.patch() PATCH 修改部分数据
axios.delete() DELETE 删除数据

Axios 只负责"发送请求和接收响应",它不负责页面跳转,也不负责把数据渲染到页面上。页面跳转由 React Router 负责,页面渲染由 React 负责。

1.2 Axios 实例是什么

直接使用 Axios 时,可以这样写:

js 复制代码
axios.get('/api/todos')
axios.get('/api/users')

当项目中的请求越来越多时,很多公共配置会重复出现。因此 Axios 提供了 axios.create(),用来创建一个提前配置好的 Axios 实例:

js 复制代码
const instance = axios.create({
  baseURL: '/api',
  timeout: 5000,
})

创建实例后,可以使用这个实例发送请求:

js 复制代码
instance.get('/todos')
instance.get('/users')

这两个请求会自动使用实例中的公共配置。

Axios 实例可以理解为一个已经设置好公共规则的请求工具。

1.3 Axios 常用配置参数

Axios 实例可以接收很多配置,其中最常用的是 baseURLtimeoutheaderswithCredentials。查询参数 params 通常写在某一次具体请求中。

参数 作用 示例
baseURL 给所有相对请求添加公共地址前缀 /api
timeout 设置请求最长等待时间,单位是毫秒 5000
headers 设置请求头 Content-TypeAuthorization
withCredentials 跨域请求时是否携带 Cookie truefalse
params 设置 URL 查询参数 page=1completed=true
data 设置请求体数据 新增或修改时提交的对象

1.3.1 baseURL 公共地址前缀

假设 Axios 实例配置为:

js 复制代码
const instance = axios.create({
  baseURL: '/api',
})

发送请求时只写:

js 复制代码
instance.get('/todos')

Axios 会把两部分组合起来:

text 复制代码
/api + /todos = /api/todos

因此浏览器最终请求的是:

text 复制代码
GET /api/todos

使用公共前缀有两个主要作用:

  • 不需要在每个请求中重复编写 /api
  • 公共地址变化时,只需要修改 Axios 实例中的一处配置。

需要注意,/api 本身不会自动连接后端。它只是一个普通的路径前缀。请求最终由 Mock、Vite 代理还是真实后端处理,要看服务器如何配置。

1.3.2 timeout 超时时间

js 复制代码
const instance = axios.create({
  timeout: 5000,
})

5000 的单位是毫秒,表示请求最多等待 5 秒。如果 5 秒后仍然没有收到响应,Axios 会结束等待并产生超时错误。

1.3.3 headers 请求头

请求头用来向服务器补充说明请求信息:

js 复制代码
const instance = axios.create({
  headers: {
    'Content-Type': 'application/json',
  },
})

Content-Type: application/json 表示请求体中的数据采用 JSON 格式。登录系统中还可能通过 Authorization 请求头携带令牌。

1.3.4 params 查询参数

查询参数用来告诉服务器需要查询什么条件的数据:

js 复制代码
instance.get('/todos', {
  params: {
    completed: true,
    page: 1,
  },
})

Axios 会将它转换为:

text 复制代码
/api/todos?completed=true&page=1

其中,completed=true 可以表示只查询已完成任务,page=1 可以表示查询第一页。查询参数经常用于搜索、筛选、排序和分页。

Cookie 是浏览器为某个网站保存的一小段数据,常用于保存登录会话标识。用户登录成功后,服务器可以让浏览器保存一个会话标识;以后浏览器再次请求服务器时,会携带这个 Cookie,服务器就能判断当前用户是谁。

当前端和后端跨域,并且登录状态依赖 Cookie 时,可以配置:

js 复制代码
const instance = axios.create({
  withCredentials: true,
})

是否需要开启它,要看项目是否使用 Cookie 登录,以及后端是否允许跨域携带凭证。

2. Vite 项目架构与依赖安装

2.1 Vite 在项目中起什么作用

Vite 是这个前端项目的开发服务器和构建工具。开发阶段执行 pnpm dev 后,Vite 会启动本地服务、处理 JavaScript 模块、转换 React JSX,并在代码变化后更新页面。

创建 React + Vite 项目可以使用:

bash 复制代码
pnpm create vite todos --template react
cd todos
pnpm i
pnpm dev

这些命令的作用如下:

命令 作用
pnpm create vite todos --template react 创建名为 todos 的 React + Vite 项目
cd todos 进入项目目录
pnpm i 安装 package.json 中声明的依赖
pnpm dev 启动 Vite 开发服务器

2.2 路由、Axios 与 Mock 依赖

在基础项目之上,需要安装路由、请求和 Mock 相关依赖:

bash 复制代码
pnpm i react-router-dom
pnpm i axios
pnpm i -D vite-plugin-mock
依赖 作用 类型
react-router-dom 管理 React 页面路由 运行依赖
axios 发送 HTTP 请求 运行依赖
vite-plugin-mock 在开发阶段模拟后端接口 开发依赖

-D 表示将依赖安装到 devDependencies。Mock 插件主要在开发阶段使用,所以通过 pnpm i -D vite-plugin-mock 安装。

安装 vite-plugin-mock 后,还需要在 vite.config.js 中引入并注册它。安装表示项目拥有了这个插件,注册表示 Vite 启动时真正使用这个插件。

3. 项目目录与各模块职责

3.1 项目结构

text 复制代码
todos-fullstack/
├── backend/
│   └── package.json
└── frontend/
    └── todos/
        ├── mock/
        │   └── todos.js
        ├── src/
        │   ├── api/
        │   │   ├── config.js
        │   │   └── todos.js
        │   ├── components/
        │   │   └── Nav.jsx
        │   ├── pages/
        │   │   ├── Home.jsx
        │   │   └── Todo.jsx
        │   ├── App.jsx
        │   ├── index.css
        │   └── main.jsx
        ├── index.html
        ├── package.json
        └── vite.config.js

各个路径的职责如下:

路径 作用
index.html 浏览器加载的 HTML 入口,提供 React 挂载节点
src/main.jsx 创建 React 根节点并渲染 App
src/App.jsx 配置整个应用的页面路由
src/components/Nav.jsx 提供 Home 和 Todos 导航链接
src/pages/Home.jsx 首页组件
src/pages/Todo.jsx Todo 页面组件,进入页面后请求数据
src/api/config.js 创建 Axios 实例,保存公共请求配置
src/api/todos.js 定义获取 Todo 数据的请求函数
mock/todos.js 声明模拟的 GET /api/todos 接口和返回数据
vite.config.js 注册 React 插件和 Mock 插件
package.json 保存项目脚本和依赖信息
backend/package.json 声明后端 Koa 依赖,目前后端业务尚未展开

整个前端可以划分为三层:

层次 相关路径 主要职责
页面层 pagescomponentsApp.jsx 页面展示、路由和组件交互
API 层 src/api 发送请求并把响应交给页面
Mock 层 mockvite.config.js 在开发阶段模拟服务器响应

3.2 index.html 与 main.jsx:应用如何启动

index.html 中最关键的部分是:

html 复制代码
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>

#root 是 React 的挂载位置,浏览器接着加载 main.jsx

js 复制代码
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() 找到页面中的 #rootrender() 再把 <App /> 渲染进去。从这里开始,React 接管页面内容。

4. React Router 页面路由

4.1 App.jsx 路由中心

App.jsx 的代码如下:

js 复制代码
import { 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.jsx'));
const Todo = lazy(() => import('./pages/Todo.jsx'));

function App() {
  return (
    // 路由接管一切
    <Router>
      <Nav />
      <Suspense fallback={<p>加载中...</p>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/todos" element={<Todo />} />
        </Routes>
      </Suspense>
    </Router>
  )
}

export default App

各部分的作用如下:

代码 作用
BrowserRouter as Router 创建路由环境,并将 BrowserRouter 简写成 Router
Routes 根据当前浏览器路径查找匹配的路由
Route 建立路径和页面组件之间的对应关系
lazy() 需要显示页面时再加载对应组件
Suspense 懒加载期间显示"加载中..."
<Nav /> 显示可以点击的导航区域

这两条 Route 建立了下面的对应关系:

浏览器路径 显示组件
/ Home
/todos Todo

Nav.jsx 的代码如下:

js 复制代码
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

NavRoute 负责不同的工作:

对象 负责什么
Nav 中的 Link 提供可以点击的入口,并修改浏览器路径
Route 路径变化后,决定显示哪个组件

点击 Todos 后,执行过程是:

text 复制代码
点击 <Link to="/todos">
→ 浏览器路径变成 /todos
→ Router 感知路径变化
→ Routes 重新匹配
→ Route 匹配 path="/todos"
→ 页面显示 Todo 组件

即使删除 <Nav />,在地址栏直接输入 /todosRoute 仍然可以匹配 Todo 页面。但是页面上将失去可点击的导航入口。

5. 项目中的 Axios API 层

5.1 config.js:统一配置怎么请求

src/api/config.js 的代码如下:

js 复制代码
//配置下axios
import axios from 'axios';
//实例化axios
//fetch 缺点是功能小
//app /api/todos->:3000/todos
//统一管理,fetch 升级为axios
const instance=axios.create({
    baseURL:'/api',//dev 前端模拟的请求地址 /api/todos
    //baseURL:'http://localhost:3300',//prod 前端模拟的请求地址 /api/todos
    timeout:5000,//超时时间
})

export default instance;

这个模块做了两件事:

  • 通过 axios.create() 创建 Axios 实例。
  • 将这个实例默认导出,供其他 API 模块使用。

当前实例只有两个配置:

配置 当前值 实际作用
baseURL /api 给相对请求添加 /api 前缀
timeout 5000 最多等待 5 秒

因此,其他模块使用这个实例请求 /todos 时,最终请求路径会变成 /api/todos

5.2 api/todos.js:定义请求什么数据

src/api/todos.js 的代码如下:

js 复制代码
//一个模块一个js文件
import axios from './config';
//api 目录的职责 提供数据接口
//不是直接就去后端 后端没有开发好 和我们分离
export const getTodos=async()=>{
    const res=await axios.get('/todos');
    return res.data;
}

这里导入的变量虽然叫 axios,但它实际是 config.js 导出的 Axios 实例。

getTodos() 执行时:

text 复制代码
axios.get('/todos')
→ 使用实例的 baseURL: '/api'
→ 最终发送 GET /api/todos

await 会等待请求完成。Axios 收到响应后,把完整响应对象赋给 resres.data 是接口返回的业务数据,因此函数最后通过 return res.data 把数据交给调用它的页面。

两个 API 模块的区别可以概括为:

模块 解决的问题
config.js 所有请求应该怎么发送
api/todos.js Todo 数据应该请求哪个路径

6. Vite Mock 模拟接口

6.1 vite.config.js 注册 Mock 插件

安装命令是:

bash 复制代码
pnpm i -D vite-plugin-mock

当前 vite.config.js 的代码如下:

js 复制代码
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { viteMockServe } from 'vite-plugin-mock'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), viteMockServe({
    mockPath:'mock',
    localEnabled: true,
  })],
  
})

其中:

配置 作用
react() 让 Vite 支持 React JSX 和热更新
viteMockServe() 将 Mock 服务注册到 Vite
mockPath: 'mock' 告诉插件去 mock 目录读取模拟接口
localEnabled: true 表示本地开发时启用 Mock

Vite 启动后,Mock 插件会读取 mock 目录中的接口配置。当浏览器发送请求时,插件会检查请求方式和 URL 是否能够匹配。

6.2 mock/todos.js 返回模拟数据

当前 Mock 代码如下:

js 复制代码
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,
                }
            ]
            }
        }
    }
]

这段配置声明了一个模拟接口:

属性 当前值 作用
url /api/todos 要匹配的请求路径
method get 只匹配 GET 请求
timeout 2000 延迟 2 秒返回数据
response 函数 返回模拟的 Todo 数据

API 层和 Mock 层并不是通过相互导入连接起来的。它们依靠请求方式和请求路径建立联系:

text 复制代码
API 层发送:GET /api/todos
Mock 层声明:GET /api/todos

两项都匹配后,Vite Mock 执行 response(),再把返回的数据作为 HTTP 响应交给 Axios。

7. Todo.jsx 与完整数据链路

7.1 Todo.jsx 进入页面后做什么

Todo.jsx 的代码如下:

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

function Todos(){
  const [todos, setTodos]=useState([]);
  useEffect(()=>{
    //IIFE 立即执行函数
    (async()=>{
      const data=await getTodos();
      setTodos(data);
    })();
  },[]);
    return(
        <>
          Todos
        </>
    )
}
export default Todos

useState([]) 创建了一个状态:

  • todos 保存 Todo 数据,初始值是空数组。
  • setTodos 用来修改 todos

useEffect(..., []) 会在 Todo 组件进入页面后执行。空数组 [] 表示这个 Effect 在组件首次挂载时运行。

Effect 内部的:

js 复制代码
(async()=>{
  const data=await getTodos();
  setTodos(data);
})();

是一个异步立即执行函数。它刚被定义就会马上运行,在这个项目中的作用是:进入 /todos 页面后立即调用 getTodos() 请求初始 Todo 数据。

7.2 data 到底来自哪里

data 并不是自动出现的,它是 getTodos() 的返回值:

js 复制代码
const data=await getTodos();

继续进入 getTodos()

js 复制代码
const res=await axios.get('/todos');
return res.data;

Mock 的 response() 返回:

js 复制代码
{
  code: 0,
  todos: [
    {
      id: 1,
      title: '学习前端接口工程',
      completed: true,
    },
    {
      id: 2,
      title: '学习后端接口工程',
      completed: false,
    },
  ],
}

Axios 接收到这份响应后,它会出现在 res.data 中。getTodos() 又执行了 return res.data,所以 Todo 页面中的 data 就是上面这个对象。

随后执行:

js 复制代码
setTodos(data);

这会把整个对象保存到 todos 状态中。当前 JSX 只显示了文字 Todos,没有读取并遍历 todos,所以数据请求可以正常完成,但页面上暂时看不到返回的任务列表。

7.3 请求消息如何走完整个项目

完整过程如下:

text 复制代码
1. 用户点击 Nav 中的 Todos
2. Link 把浏览器页面路径改成 /todos
3. Route 匹配 /todos 并显示 Todo.jsx
4. Todo 组件首次挂载,useEffect 开始执行
5. 异步立即执行函数调用 getTodos()
6. getTodos() 调用 Axios 实例的 get('/todos')
7. baseURL '/api' 与 '/todos' 组合成 /api/todos
8. 浏览器发送 GET /api/todos
9. Vite Mock 按照 GET 和 /api/todos 匹配接口
10. response() 返回模拟数据
11. Axios 把响应保存到 res
12. getTodos() 返回 res.data
13. Todo.jsx 中的 data 接到这个返回值
14. setTodos(data) 更新状态

这里存在两种不同的路径:

路径 处理者 作用
/todos React Router 决定显示 Todo 页面
/api/todos Vite Mock 返回 Todo JSON 数据

/todos 是页面路由,/api/todos 是数据接口。Axios 发送的是数据请求,不是页面跳转。

8. 当前全栈项目处于什么阶段

目前前端已经能够完成下面的闭环:

text 复制代码
页面路由
→ Todo 组件
→ Axios API 层
→ Mock 接口
→ 返回模拟数据
→ 更新 React 状态

后端目录目前主要声明了 Koa 依赖,真正的 Koa 服务、Todo 路由和数据库逻辑还没有展开。因此当前阶段由 Vite Mock 临时承担数据接口的角色。

将来后端完成后,只要真实后端也能够接收对应请求并返回相同结构的数据,API 层就可以继续作为页面和后端之间的连接层。

总结

这个 Todos 项目由几块职责明确的模块组成:Vite 启动开发环境并加载插件,React Router 使用 /todos 控制页面显示,Axios 负责发送 HTTP 请求,config.js 保存公共请求配置,api/todos.js 定义 Todo 请求,Vite Mock 则通过 GET + /api/todos 匹配请求并返回模拟数据。进入 Todo 页面后,useEffect 中的异步立即执行函数调用 getTodos(),Axios 等待 Mock 响应,getTodos() 返回 res.data,最终由 setTodos(data) 保存到 React 状态。理解这些模块之间的职责边界,就能看清前端页面、请求层和模拟服务是如何组合成一条完整数据链路的。

相关推荐
Elastic 中国社区官方博客1 小时前
用两行 JSON 替换你的 ILM 策略:数据流生命周期新增冻结层支持
大数据·运维·elasticsearch·搜索引擎·架构·全文检索
亿元程序员1 小时前
项目经理让我在 2D 项目里做 3D 音乐球球,我只好做了个“假的”
前端
糖果店的幽灵1 小时前
Codex官网前端可抄吗?从模仿到创新的技术实践指南
前端·人工智能
youqingyike1 小时前
【无标题】
前端·qt
@卓越俊逸_角立杰出@1 小时前
java实现Agent+ReAct demo(Spring Boot + Spring AI Alibaba ReAct Agent)
java·spring·react.js
小张同学a.1 小时前
ELK企业级日志分析平台3——ES数据备份 & 集群监控 & ELFK+Kafka 架构部署
linux·运维·elk·elasticsearch·架构·kafka·filebeat
恋猫de小郭1 小时前
Jetpack Compose 8 月版正式发布,核心模块 1.12
android·前端·flutter
纵道软件1 小时前
使用 Docker 构建自定义 SeaTunnel Web 服务镜像
前端·docker·容器
山荷枝1 小时前
05-Vue
前端·javascript·vue.js