三种能优化React应用架构的TypeScript模式
了解Repository、Observer和Builder模式如何利用TypeScript的类型系统来构建更简洁、更易维护的React和Next.js代码库。
编写 TypeScript 并不意味着就能很好地使用它
许多开发者只是在变量上加上类型注解就认为万事大吉。但 TypeScript 的真正优势在于其他方面——那些能够将脆弱的代码库与随时间稳步发展的代码库区分开来的结构化模式。
通过使用 React 和 Next.js 开发过多个生产级应用,我发现有几种模式真正具有颠覆性作用。这些并非抽象的教科书练习,而是针对你实际会遇到的问题的解决方案。
1. 数据库模式——将数据获取与其他功能分离
当组件直接调用 fetch() 时,一旦 API 结构发生变化,你就不得不重新编写代码,这会带来极大的麻烦。
数据库模式通过一个简洁的接口来封装数据访问:
// repositories/userRepository.ts
interface UserRepository {
getById(id: string): Promise<User>;
getAll(): Promise<User[]>;
}
export class ApiUserRepository implements UserRepository {
async getById(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
async getAll(): Promise<User[]> {
const res = await fetch('/api/users');
return res.json();
}
}
通过这种方式,您的组件依赖的是抽象层而非具体实现。这意味着在测试中您可以用模拟版本替换ApiUserRepository,而无需修改任何UI代码。
2. 观察者模式——无需Redux即可实现响应式状态
Redux确实能完成任务,但对于那些并不特别复杂的状态来说,使用它就如同用大锤去处理小问题一般。基于类型化事件发射器构建的轻量级、基于类的观察者实现则提供了一种更简单的替代方案:
// utils/eventBus.ts
type EventMap = {
'user:loggedIn': { userId: string };
'cart:updated': { itemCount: number };
};
class TypedEventBus {
private listeners: Partial<{
[K in keyof EventMap]: ((payload: EventMap[K]) => void)[]
}> = {};
on<K extends keyof EventMap>(event: K, cb: (payload: EventMap[K]) => void) {
(this.listeners[event] ??= []).push(cb);
}
emit<K extends keyof EventMap>(event: K, payload: EventMap[K]) {
this.listeners[event]?.forEach(cb => cb(payload));
}
}
export const eventBus = new TypedEventBus();
这里的所有内容都是强类型定义的——没有隐藏在背后的any类型。因此,对于某个监听器应该接收何种格式的负载数据,不存在任何歧义。
3. 构建器模式——让复杂对象的创建更有条理
如果你曾为构建包含嵌套条件语句的过滤器对象、API请求配置或表单模式而苦恼,那么构建者模式能立即带来清晰度:
// builders/queryBuilder.ts
class QueryBuilder {
private params: Record<string, string> = {};
withPage(page: number) {
this.params['page'] = String(page);
return this;
}
withLimit(limit: number) {
this.params['limit'] = String(limit);
return this;
}
withSearch(term: string) {
if (term.trim()) this.params['q'] = term;
return this;
}
build(): string {
return new URLSearchParams(this.params).toString();
}
}
// Usage
const query = new QueryBuilder()
.withPage(1)
.withLimit(20)
.withSearch('typescript')
.build();
// → "page=1&limit=20&q=typescript"
生成的代码读起来很自然,各部分衔接流畅,从结构上就避免了以错误顺序调用各个步骤的情况。
核心要点
- 仓库模式:遵循依赖倒置原则,将数据访问层与用户界面逻辑分离
- 观察者模式:无需大量冗余代码即可实现应用各组件之间的轻量级响应式通信
- 构建者模式:让复杂对象的构建既易于理解又安全可靠
- 这三种模式都能从TypeScript的接口和泛型中获益匪浅,使其比纯JavaScript版本更加整洁
“TypeScript 不仅仅是添加类型而已——它还能在应用程序的每一层之间建立沟通机制。”——《Effective TypeScript》一书作者 Dan Vanderkam
下一步可尝试的内容
本周请从本文中挑选一种模式,并将其应用到代码库中的实际模块中。一次只处理一个部分,记住这些模式只是工具而非教条。
最优秀的代码库并非取决于其中包含多少类型,即便在 TypeScript 项目中也是如此。它们的优势在于设计的合理性——每个模式的存在都是因其确实有存在的必要。
相关阅读
- TypeScript的Go编译器与原生执行:迁移指南 — 了解基于Go的TypeScript编译器及Node.js原生执行方式将如何影响React、Next.js项目,以及当前应在tsconfig中做哪些调整。
- 从NestJS Swagger自动生成类型安全的Next.js API客户端 — 学习如何利用NestJS Swagger与Orval自动为Next.js生成类型安全的React Query钩子,从而避免API类型的重复。