前端怎么配合后端完成数据对接

前端配合后端完成数据对接,和"接口联调"有重叠,但侧重点不同:

  • 接口联调:偏过程,重点是"接口能不能通、问题怎么排查"。
  • 数据对接:偏内容,重点是"数据长什么样、怎么传、怎么转、怎么存、怎么保证一致"。

一句话:

联调是让接口通,数据对接是让数据对、准、稳、可维护。

数据对接做不好,常见后果是:字段对不上、格式混乱、空值崩溃、精度丢失、状态不一致、改一处崩多处。

下面按"契约 → 格式 → 数据层 → 校验 → 状态 → 安全 → 流程"讲。


一、先定数据契约

数据对接的第一步不是写代码,而是和后端把"数据长什么样"定清楚。

1. 字段层面

每个字段至少明确:

  • 字段名
  • 类型:string、number、boolean、array、object、null
  • 是否必填
  • 默认值
  • 示例值
  • 含义说明
  • 枚举值范围

示例:

json 复制代码
{
  "id": 1,
  "name": "张三",
  "status": 1,
  "amount": 99.9,
  "createdAt": "2026-10-17 10:30:00",
  "tags": ["vip", "new"]
}

2. 结构层面

  • 是否统一包裹:{ code, message, data }
  • 分页结构:list、total、page、pageSize
  • 嵌套层级:是否有多层对象
  • 数组元素结构是否稳定
  • 空数据返回 null、[] 还是 {}

3. 错误层面

  • 错误码表
  • 错误信息格式
  • 业务错误和系统错误区分
  • 字段级错误怎么返回

4. 推荐工具

  • Apifox / YApi / Swagger
  • OpenAPI 生成 TS 类型
  • JSON Schema 校验

二、统一数据格式

前后端最容易扯皮的就是格式。建议项目一开始就定死。

1. 时间

  • 统一用字符串:YYYY-MM-DD HH:mm:ss
  • 或统一用时间戳:毫秒
  • 明确时区
  • 前端用 dayjs 统一格式化
ts 复制代码
import dayjs from 'dayjs'
export const formatTime = (val: string | number) =>
  dayjs(val).format('YYYY-MM-DD HH:mm:ss')

2. 金额

  • 后端返回分,前端转元;或后端返回字符串
  • 不要用浮点数直接计算
  • 用 decimal.js 或整数运算
ts 复制代码
import Decimal from 'decimal.js'
export const fenToYuan = (fen: number) => new Decimal(fen).div(100).toFixed(2)

3. 枚举

  • 后端返回 code,前端维护映射表
  • 映射表统一放 constants 或字典接口
  • 不要在前端写死中文
ts 复制代码
export const ORDER_STATUS = {
  0: '待支付',
  1: '已支付',
  2: '已发货',
  3: '已完成'
} as const

4. ID

  • 大整数 ID 用字符串返回,避免 JS 精度丢失
  • 前后端统一 ID 类型

5. 布尔

  • 统一用 true/false
  • 不要混用 0/1、"Y"/"N"

6. 空值

  • 空数组返回 [],不要返回 null
  • 空对象返回 {} 或 null 要约定
  • 字符串空值统一 "" 还是 null

7. 分页

统一格式:

json 复制代码
{
  "list": [],
  "total": 100,
  "page": 1,
  "pageSize": 10
}

三、设计前端数据层

不要页面直接调 axios 拿数据就用。中间加一层,做转换、校验、兜底。

推荐分层:

text 复制代码
页面/组件
  ↓
composables / hooks
  ↓
api 层(请求)
  ↓
adapter / mapper(数据转换)
  ↓
model / types(类型)

1. API 层

只负责请求,不处理业务:

ts 复制代码
// api/user.ts
import request from '@/utils/request'

export const getUserList = (params: UserListParams) =>
  request.get<PageResult<UserDTO>>('/user/list', { params })

export const getUserDetail = (id: string) =>
  request.get<UserDTO>(`/user/${id}`)

2. DTO 与 Model 分离

  • DTO:后端返回的原始结构
  • Model:前端页面真正使用的结构
ts 复制代码
// types/user.ts
export interface UserDTO {
  id: string
  user_name: string
  status: number
  created_at: string
}

export interface UserModel {
  id: string
  name: string
  status: 'active' | 'disabled'
  createdAt: string
}

3. Adapter 做转换

把后端数据转成前端模型:

ts 复制代码
// adapters/user.ts
import type { UserDTO, UserModel } from '@/types/user'

export function toUserModel(dto: UserDTO): UserModel {
  return {
    id: dto.id,
    name: dto.user_name || '-',
    status: dto.status === 1 ? 'active' : 'disabled',
    createdAt: dto.created_at
  }
}

这样后端改字段,只改 adapter,页面不用动。

4. 统一处理空值和异常

ts 复制代码
export function safeArray<T>(val: T[] | null | undefined): T[] {
  return Array.isArray(val) ? val : []
}

export function safeObject<T>(val: T | null | undefined, fallback: T): T {
  return val ?? fallback
}

四、数据校验与容错

后端数据不一定可信,前端要做兜底。

1. 类型校验

  • 用 TypeScript 定义类型
  • 运行时用 Zod / Valibot 校验关键数据
ts 复制代码
import { z } from 'zod'

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  status: z.number()
})

const data = UserSchema.parse(res.data)

2. 空值兜底

ts 复制代码
const list = res.data?.list ?? []
const total = res.data?.total ?? 0

3. 枚举兜底

未知枚举值显示"未知",不要显示 undefined。

4. 异常数据

  • 图片 URL 为空,显示占位图
  • 时间格式错,显示 -
  • 金额为 null,显示 0.00

五、数据状态管理

数据拿到后,怎么存、怎么更新、怎么同步。

1. 页面级数据

列表、表单、弹窗数据放页面内,用 ref / reactive。

2. 全局数据

用户信息、权限、字典放 Pinia:

ts 复制代码
export const useUserStore = defineStore('user', () => {
  const userInfo = ref<UserModel | null>(null)
  async function fetchUserInfo() {
    const dto = await getUserInfo()
    userInfo.value = toUserModel(dto)
  }
  return { userInfo, fetchUserInfo }
})

3. 缓存策略

  • 字典、配置类数据缓存到 localStorage 或 Pinia
  • 列表数据不缓存,每次刷新
  • 详情数据可按 ID 缓存

4. 数据刷新

  • 增删改后刷新列表
  • 用 refresh() 或重新请求
  • 避免手动改本地数组导致不一致

5. 乐观更新

体验要求高时,先更新 UI,失败再回滚:

ts 复制代码
const old = list.value.slice()
list.value = list.value.filter(i => i.id !== id)
try {
  await deleteUser(id)
} catch {
  list.value = old
}

6. 并发与重复提交

  • 按钮 loading
  • 请求取消(AbortController)
  • 防抖节流

六、数据安全

1. 敏感数据脱敏

手机号、身份证、银行卡前端展示时脱敏:

ts 复制代码
export const maskPhone = (phone: string) =>
  phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')

2. 权限控制

  • 后端返回权限码
  • 前端控制菜单、按钮、字段显示
  • 但接口权限必须后端校验,前端只做展示控制

3. 不要信任前端

  • 金额、库存、状态等关键数据以后端为准
  • 前端计算结果只做展示,提交时后端重新校验

4. 防 XSS

  • 用户输入内容渲染时转义
  • 富文本用 DOMPurify 清洗

七、数据一致性

1. 前后端字段一致

  • 用 OpenAPI 生成类型
  • 联调后更新文档
  • 字段变更走通知

2. 多端一致

  • Web、H5、小程序共用接口时,格式统一
  • 枚举、字典从接口获取,不要各端写死

3. 缓存一致

  • 更新数据后清缓存
  • 关键数据加版本号
  • 避免 localStorage 脏数据

4. 状态一致

  • 列表和详情数据同步
  • 编辑后返回列表要刷新
  • 跨页面操作要通知

八、对接流程建议

  1. 定契约:字段、类型、格式、错误码、分页。
  2. 生成类型:OpenAPI 转 TS。
  3. Mock 开发:前端先跑通。
  4. 写 API 层:统一请求封装。
  5. 写 Adapter:DTO 转 Model。
  6. 页面接入:处理加载、空、错误。
  7. 联调排错:Network、抓包、请求 ID。
  8. 补异常:空值、超时、无权限、重复提交。
  9. 更新文档:契约变更同步。
  10. 回归测试:主流程 + 边界。

九、常见坑

  • 字段名前后端不一致:userName vs user_name
  • 类型不一致:数字传成字符串
  • 大整数 ID 精度丢失
  • 金额用浮点数计算
  • 时间格式混用、时区错
  • 枚举各端写死,改一处漏一处
  • 空数组返回 null,页面崩溃
  • 分页格式不统一
  • 错误码各写各的,无法统一处理
  • 页面直接调 axios,没有数据层
  • 后端改字段,前端大面积改
  • 前端信任后端数据,不做兜底
  • 缓存不清理,显示旧数据

十、总结

前端配合后端完成数据对接的核心:

契约先行、格式统一、数据分层、转换隔离、校验兜底、状态可控、安全可信、文档同步。

一个健康的数据对接应该做到:

  • 字段、类型、格式有明确约定
  • 后端数据通过 adapter 转成前端模型
  • 页面不直接依赖后端原始字段
  • 空值、异常、错误码有统一处理
  • 关键数据有类型校验
  • 缓存和刷新策略清晰
  • 敏感数据脱敏,权限后端校验
  • 契约变更及时同步

如果你告诉我你们用的接口工具和项目类型,我可以给你一份更具体的数据对接清单和 adapter 代码模板。

相关推荐
lexiangqicheng1 小时前
Docker的沙箱机制是怎样的?
前端·javascript
燕落南枝2 小时前
HTML 表格标签与 AI 合并单元格实战指南
前端·人工智能·html
zyseo82 小时前
谷歌SEO 内链地图脚本:Python 画出权重分发,揪出孤岛页
java·前端·python
ClickHouseDB3 小时前
ClickHouse .NET 驱动程序新特性:从 1.0 到 1.3 的演进之路
java·前端·windows
bitbrowser3 小时前
Gmail注册要求手机号如何解决?哪些情况可以不填手机号
前端·指纹浏览器·gmail
LCG米3 小时前
STM32 HardFault故障定位实测:压栈帧寄存器解析、CFSR故障源判读与出错地址回溯
前端·javascript·stm32·单片机·嵌入式
夏幻灵3 小时前
Express 静态资源托管详解:从本地 HTML 到 express.static()
前端·html·express
yyuuuzz3 小时前
云服务器部署 web 项目的进程常驻记录
运维·服务器·前端·阿里云·云计算·aws