news 2026/9/25 0:50:41

RN + TypeScript 项目越写越乱?如何规范架构?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RN + TypeScript 项目越写越乱?如何规范架构?

@[toc]

如果你在 RN 项目里用了 TypeScript,但还是经常遇到下面这些情况,那你大概率不是一个人:

  • 业务一多,代码开始到处飞,找逻辑像在翻垃圾堆
  • hooks 套 hooks,useEffect 里再调 useEffect,自己都不敢改
  • 类型文件一堆,但和真实实现经常对不上
  • 新人一来就问:“这个状态到底在哪儿改的?”

这篇文章不是教你“TypeScript 怎么写”,而是教你:
在 RN 项目里,TypeScript 怎么和架构一起用,才能不失控。

一、为什么 RN + TS 项目特别容易“写乱”?

先说一个结论:

项目乱,不是因为 TypeScript,而是“职责边界不清 + 状态乱流”。

1. 业务增长,逻辑开始横向扩散

最常见的场景:

  • 页面里写请求
  • hook 里也写请求
  • redux 里再写一套
  • utils 里还有一份“备用逻辑”

结果是:
一个需求改 4 个地方,还不一定改全。

2. Hooks 层级复杂,本来是解耦,最后变成“黑盒”

你可能写过类似这样的代码:

functionusePageLogic(){constdata=useFetch()constresult=useProcess(data)useEffect(()=>{doSomething(result)},[result])}

问题不是 hooks 多,而是:

  • hooks 里混了业务规则
  • hooks 之间有隐式依赖
  • 调试时根本不知道是哪一层出问题

3. 类型与实现分离,最后谁都不信类型

常见症状:

  • interface 写得很全
  • 实际接口返回偷偷多字段 / 少字段
  • any 越用越多
  • 最后 TS 只剩“自动补全工具”的作用

二、一个能长期维护的 RN + TS 项目结构

先给你一个推荐结构总览,这是我在中大型 RN 项目里反复验证过的。

src/ ├── domain/ // 业务模型 & 业务规则 ├── service/ // API / SDK / 数据来源 ├── hooks/ // 可复用 hooks ├── store/ // 全局状态 ├── ui/ // 纯 UI 组件 ├── pages/ // 页面(只组装,不写重逻辑) ├── utils/ └── types/

接下来我们逐层拆。

三、domain 层:业务规则的“唯一入口”

domain 是整个项目的核心。

你应该在 domain 里放什么?

  • 业务实体(User、Order)
  • 业务规则(状态转换、校验)
  • 与 UI、网络无关的逻辑

示例:用户领域模型

// domain/user.tsexportinterfaceUser{id:stringname:stringrole:'admin'|'user'}exportfunctionisAdmin(user:User){returnuser.role==='admin'}

关键点:

  • domain 不 import RN、API、store
  • domain 只关心“业务正确性”

四、service 层:所有“数据来源”的统一出口

service 只做一件事:

把外部世界的数据,变成 domain 能用的数据。

示例:请求用户接口

// service/userService.tsimport{User}from'@/domain/user'exportasyncfunctionfetchUser():Promise<User>{constres=awaitfetch('/user')returnres.json()}

不要在页面里直接 fetch。

五、hooks 层:封装“状态 + 行为”,但不写业务规则

一个非常重要的原则:

hooks = orchestration,不是 business logic。

示例:useUser

// hooks/useUser.tsimport{useEffect,useState}from'react'import{fetchUser}from'@/service/userService'import{User}from'@/domain/user'exportfunctionuseUser(){const[user,setUser]=useState<User|null>(null)useEffect(()=>{fetchUser().then(setUser)},[])returnuser}

如果你发现 hook 里开始写if (role === 'admin')
那说明:逻辑该下沉到 domain 了。

六、UI 层:彻底“无脑”的组件

UI 组件只负责:

  • 接 props
  • 渲染
  • 触发回调

示例:用户卡片

type Props = { name: string onPress: () => void } export function UserCard({ name, onPress }: Props) { return ( <Pressable onPress={onPress}> <Text>{name}</Text> </Pressable> ) }

UI 层不 import hooks、不 import service。

七、页面(Page):只做“组装”

页面是“最脏但最轻”的一层。

export function UserPage() { const user = useUser() if (!user) return null return ( <UserCard name={user.name} onPress={() => {}} /> ) }

页面:

  • 可以用 hook
  • 可以用 store
  • 但不写核心逻辑

八、全局状态方案怎么选?

这是 RN 项目里非常容易踩坑的一点。

Redux Toolkit

适合:

  • 状态多
  • 多页面共享
  • 对可预测性要求高

优点:

  • 规范
  • DevTools 强

缺点:

  • 样板代码略多

Zustand

适合:

  • 中小项目
  • 状态简单
  • 希望写得快
constuseStore=create(set=>({count:0,inc:()=>set(state=>({count:state.count+1}))}))

Recoil

适合:

  • 状态依赖复杂
  • 类似图结构

但心里要有数:生态和长期维护要评估。

九、TypeScript 在 RN 项目的最佳实践

1. 类型从 domain 开始

不要一上来就写:

typeProps=any

类型应该从业务模型流出。

2. 禁止“到处定义重复类型”

统一出口:

exporttype{User}from'@/domain/user'

3. 不要为了 TS 而 TS

有些地方允许不完美:

constref=useRef<any>(null)

关键在于:核心业务链路一定要有类型。

十、常见反模式(请尽量避免)

1. 滥用 useEffect 做状态管理

useEffect(()=>{setA(calcB(b))},[b])

这种写多了,状态会完全失控。

2. 一个 hook 管一切

usePageLogic()

内部 500 行代码,没人敢碰。

3. domain 被 UI 污染

import{Alert}from'react-native'

一旦 domain import RN,架构就开始塌。

十一、真实项目里的收益

在真实 RN 项目中,用这套结构后:

  • 新需求平均开发时间 ↓ 30%+
  • Bug 定位明显更快
  • 新人 1~2 天能独立改需求
  • TS 不再是“摆设”

最后的总结

如果你只记住一句话:

RN + TypeScript 的核心不是“写类型”,而是“让结构帮你兜住复杂度”。

TypeScript 是放大器:

  • 架构好 → TS 越用越爽
  • 架构乱 → TS 只会更痛苦
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 1:50:13

创建多行文本框

多行文本框&#xff08;Multiline Text Box&#xff09;允许用户输入多行文本&#xff0c;广泛应用于需要大量文本输入的场景&#xff0c;例如即时通讯、笔记应用以及文本编辑器等。与单行文本框相比&#xff0c;多行文本框提供更丰富的交互体验&#xff0c;支持多行内容的显示…

作者头像 李华
网站建设 2026/9/24 23:03:05

Docker MCP 网关注册延迟高达30秒?,紧急排查与毫秒级响应优化方案

第一章&#xff1a;Docker MCP 网关服务注册延迟问题概述在基于微服务架构的分布式系统中&#xff0c;Docker MCP&#xff08;Microservice Control Plane&#xff09;网关承担着服务发现、路由转发与负载均衡的核心职责。当新启动的服务实例未能及时注册到网关时&#xff0c;会…

作者头像 李华
网站建设 2026/9/23 21:13:18

Java 线程状态详解:从观察到理解

一、观察线程的所有状态在 Java 中&#xff0c;线程的状态是由 Thread.State 枚举定义的&#xff0c;一共有 6 种状态。这些状态代表了线程从创建到销毁的各个阶段。我们可以通过 Thread.getState() 方法来获取当前线程的状态。以下是所有线程状态的列表&#xff1a;NEW&#x…

作者头像 李华
网站建设 2026/9/24 11:07:15

Spring Boot 应用开发:从理论到实践的成长之旅

在本学期 Spring Boot 应用开发课程的学习中&#xff0c;我完成了从 “对后端开发一知半解” 到 “能独立搭建完整 Web 项目” 的蜕变。这门课程不仅让我掌握了 Spring Boot 的核心技术&#xff0c;更教会了我用工程化思维解决实际问题&#xff0c;以下是我结合理论学习与实践操…

作者头像 李华
网站建设 2026/9/24 10:29:10

在Zabbix Server使用PHP编译一个扩展模块

场景&#xff1a;一次性返回所有在线 Agent 的 CPU 利用率平均值&#xff08;%&#xff09; Zabbix Server的PHP相关目录 # 一台已经跑通的 Zabbix 7.0 实例&#xff08;Server Web Agent&#xff09; # 参考 CentOS 7 源码编译路径&#xff1a; PHP_PREFIX/usr/local/zabbix…

作者头像 李华
网站建设 2026/9/24 11:10:07

Dify与Spring AI版本适配实战指南(兼容性问题全收录)

第一章&#xff1a;Dify与Spring AI版本兼容性概述在构建基于AI的现代企业级应用时&#xff0c;Dify 与 Spring AI 的集成成为关键的技术选型之一。两者在生态整合、模型调用抽象和微服务架构支持方面具备天然契合点&#xff0c;但其版本间的兼容性直接影响系统的稳定性与功能可…

作者头像 李华