TypeScript 架构实践:从后端接口到 UI 渲染数据流的完整方案

介绍在 TS 中 DTO 与 VO 的思想碰撞与实践,系统性地梳理从后端接口定义前端业务消费的完整链路。

一、 为什么要分 DTO 和 VO?

在 TypeScript 开发中,直接将后端返回的 JSON 数据透传到 UI 界面是研发初期的"捷径",但往往也是后期维护的"噩梦"。为了解决后端字段多变、命名风格不统一(如蛇形命名 vs 驼峰命名)等问题,引入 DTO(数据传输对象)VO(视图对象) 的概念至关重要。

二、 核心思想:职责分离

我们将后端接口数据在系统中的流转拆分为三个关键环节:

环节 承载载体 核心职责
入口层 DTO (Interface) 契约。严格对齐接口协议,描述后端发来的原始数据。
转换层 Mapper 函数 解耦。负责逻辑清洗、字段重命名、类型转换。
应用层 VO (Interface) 纯净。仅包含 UI 层渲染所需的属性,命名符合前端规范。

三、 实战演练:Axios 与泛型的完美结合

为了实现自动化的类型推导,我们通过泛型封装通用的响应结构。

1. 定义通用响应壳子

TypeScript 复制代码
// 统一后端返回的 JSON 格式
export interface ApiResponse<T = any> {
  code: number;
  message: string;
  data: T; // 这里的 T 将被具体的 DTO 替换
}

2. 定义 DTO 与 VO

利用 TS 工具类型(如 Pick)提高定义效率。

TypeScript 复制代码
// 后端原始 DTO
export interface UserDTO {
  id: number;
  user_name: string;
  avatar_url: string;
  created_at: number; // 秒级时间戳
}

// 前端业务 VO
// 挑出需要的字段,并增加/修改业务字段
export type UserVO = Pick<UserDTO, 'id' | 'avatar_url'> & {
  displayName: string;
  regDate: Date; // 转换为 JS Date 对象
};

3. Axios 请求与 Mapper 转换

将 Axios 的泛型能力与转换函数串联起来:

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

axios.interceptors.response.use((response) => {
  const res = response.data as ApiResponse;
  if (res.code !== 200) {
    // 统一弹出后端给的错误提示
    showToast(res.message);
    return Promise.reject(new Error(res.message));
  }
  return response;
});

// 1. API 层
const fetchUserApi = (id: string) => {
  // 告知 Axios:返回值的 body 是 ApiResponse 结构,其 data 属性是 UserDTO
  return axios.get<ApiResponse<UserDTO>>(`/api/user/${id}`);
};

// 2. Mapper 层:负责 DTO -> VO 的脏活累活
const toUserVO = (dto: UserDTO): UserVO => ({
  id: dto.id,
  avatar_url: dto.avatar_url,
  displayName: dto.user_name || '未知用户',
  regDate: new Date(dto.created_at * 1000)
});

// 3. Service 层:业务组装
async function getUserDetail(id: string): Promise<UserVO> {
  const { data: res } = await fetchUserApi(id);
  // res.data 此时被自动推导为 UserDTO
  return toUserVO(res.data);
}

// 4. UI 层(Vue/React):直接使用 UserVO
const [user, setUser] = useState<UserVO | null>(null);

useEffect(() => {
  getUserDetail('123').then(setUser);
}, []);

总结

这套架构本质上是在前端构建了一道 "类型防火墙"

  • 防火墙外:是不可控的后端原始数据(DTO)。
  • 防火墙内:是稳定的、符合业务习惯的干净数据(VO)。
  • 防火墙中间:是透明的转换逻辑(Mapper)。

通过这种设计,即便后端接口字段发生变更,你只需修改 DTO 定义和 Mapper 函数,UI 层的代码完全不需要改动。

相关推荐
小蚂蚁i3 小时前
把 TypeScript 内置工具类型彻底搞懂,顺便自己封几个好用的
前端·typescript
维基框架3 小时前
当Prompt编辑变成训练过程:SkillOpt的逆直觉方案
前端
KaMeidebaby3 小时前
卡梅德生物技术快报|小 RNA 适配体合成 + 多方法亲和力表征全流程标准化操作手册
前端·网络·数据库·人工智能·算法
爸爸6193 小时前
07 ArkTS 基础类型与接口定义:从 TypeScript 到鸿蒙的类型升级
javascript·华为·typescript·harmonyos·鸿蒙系统
OpenTiny社区4 小时前
Coding Agent 到底是怎么工作的,它又为什么会在复杂代码库里失真?
前端·github·agent
奇奇怪怪的4 小时前
Graph RAG:知识图谱增强检索
前端
默_笙4 小时前
🌏 MCP 是 AI 界的 USB-C:一个协议让所有模型都能用上所有工具
前端·javascript
以和为贵5 小时前
🔥前端也能搞懂流式输出:从 SSE 到打字机效果
前端·人工智能·架构
阿成学长_Cain5 小时前
Linux telinit 命令详解:运行级别切换|关机重启|系统维护一站式掌握
linux·运维·前端·网络
Patrick_Wilson6 小时前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化