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 层的代码完全不需要改动。

相关推荐
lichenyang45312 分钟前
鸿蒙首页从等高商品 Grid 到双列瀑布流:同一张图也能做出小红书式浏览节奏
前端
里欧跑得慢14 分钟前
Flutter主题与样式详解
前端·css·flutter·web
计算机魔术师18 分钟前
TPU推理性价比翻50%,NVIDIA的护城河要见底了?
前端
IT_陈寒38 分钟前
Python的多线程就是个假把式,我算是体验到了
前端·人工智能·后端
OpenTiny社区1 小时前
【直播分享】GenUI SDK 技术公开课第二讲 | GenuiChat 核心配置深度解析
前端·github
计算机魔术师1 小时前
kernel.org 每天 600 万次请求,98% 不是人点的
前端
今日无bug1 小时前
从0到1手撕流式输出:Vue3 + Vite 实现 LLM 流式响应全解析
前端·vue.js·vite
光影少年2 小时前
react navite跨端项目的痛点、踩过哪些坑、怎么解决
前端·react native·react.js
默_笙2 小时前
🧀 用户访问一个网站到底经历了什么?全栈部署的"城市观光"指南
前端·javascript
aixingpan2 小时前
aixingpan.cn API开发文档:api_docs_trichart_natal_composite_transit2接口指南
前端·php