# 什么是 BFF

BFF 全称是 Backend For Frontend (服务于前端的后端),也就是服务器设计 API 时会考虑前端的使用,并在服务端直接进行业务逻辑的处理,又称为用户体验适配器。BFF 只是一种逻辑分层,而非一种技术,虽然 BFF 是一个新名词,但它的理念由来已久。

# BFF 解决了什么问题

在实际项目中,经常会有某个页面需要向多个不同的服务发送请求,然后将请求回来的数据用来渲染页面不同的组件的情况,即一个页面同时存在多个请求的场景。假设每次访问一个页面都需要发送 3 个请求,同时为了保障 Android,iOS,以及 Web 端的不同需求,需要为不同的平台写不同的 API 接口,而每当值发生一些变化时,需要 Android,iOS,Web 做出修改。这样的代价显然是相当大的,也相当麻烦。

因此,我们就需要 BFF 作为中间层,在这个中间层上做一些业务逻辑处理。当有了 BFF 这一层之后,我们就不需要考虑系统后端的迁移了,后端发生的变化都可以在 BFF 层做一些响应修改。像上面所说的场景,加入了 BFF 层之后,原本每次访问发送 3 个请求,现在就变成一个请求了。

# BFF 的好处

  • 为特定前端定制 API 接口

  • 聚合多个后端服务的数据

  • 处理认证授权、日志记录等横切关注点

  • 提供更好的错误处理和缓存策略

  • 减少前端的业务逻辑复杂度

什么是横切关注点

横切关注点(Cross-cutting Concerns)指的是那些贯穿整个应用多个模块的功能,比如认证、授权、日志、缓存等。

# 如何正确使用 BFF

  • 多端应用

我们在设计 API 时会考虑到不同设备的需求,也就是为不同的设备提供不同的 API,虽然它们可能是实现相同的功能,但因为不同设备的特殊性,它们对服务端的 API 访问也各有其特点,需要区别处理。

  • 服务聚合

随着微服务的兴起,原本在同一个进程内运行的业务流程被拆分到了不同的服务中。这在增加业务灵活性的同时,也让前端的调用变得更复杂。BFF 的出现为前端应用提供了一个对业务服务调用的聚合点,它屏蔽了复杂的服务调用链,让前端可以聚焦在所需要的数据上,而不用关注底层提供这些数据的服务。

  • 非必要,莫新增

我们在看到 BFF 带来的各种好处的同时,也要注意到它所带来的代码重复和工作量增加方面的问题。如果与已有 BFF 功能类似,且展现数据的要求也相近的话,一定要谨慎对待新增 BFF 的行为。因此,建议非必要,莫新增。

# BFF 的实战应用

  • 访问控制

例如,服务中的权限控制,将所有服务中的权限控制集中在 BFF 层,使下层服务更加纯粹和独立。

  • 应用缓存

项目中时常存在一些需要缓存的临时数据,此时 BFF 作为业务的汇聚点,距离用户请求最近,遂将该缓存操作放在 BFF 层。

  • 第三方入口

在业务中需要与第三交互时,将该交互放在 BFF 层,这样可以只暴露必要信息给第三方,从而便于控制第三方的访问。

# SOLID 设计原则

SOLID 是面向对象设计的五大基本原则。

# S - 单一职责原则 (Single Responsibility Principle)

一个组件只做一件事

❌ 错误:一个组件做太多事

// 这个组件又要显示用户,又要获取数据,又要处理样式
const UserComponent = () => {
  const [user, setUser] = useState(null);
  
  // 获取数据
  useEffect(() => {
    fetch('/api/user').then(res => res.json()).then(setUser);
  }, []);
  
  // 处理样式
  const getStatusColor = (status) => {
    return status === 'active' ? 'green' : 'red';
  };
  
  // 显示界面
  return (
    <div style={{ color: getStatusColor(user?.status) }}>
      {user?.name}
    </div>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

✅ 正确:拆分成多个组件,各司其职

// 1. 获取数据的 Hook
const useUser = () => {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetch('/api/user').then(res => res.json()).then(setUser);
  }, []);
  return user;
};

// 2. 处理样式的工具
const getStatusColor = (status) => {
  return status === 'active' ? 'green' : 'red';
};

// 3. 只负责显示的组件
const UserDisplay = ({ user }) => (
  <div style={{ color: getStatusColor(user.status) }}>
    {user.name}
  </div>
);

// 4. 组合使用
const UserComponent = () => {
  const user = useUser();
  return user ? <UserDisplay user={user} /> : <div>加载中...</div>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# O - 开闭原则 (Open/Closed Principle)

组件应该对扩展开放,对修改关闭

❌ 错误:每次新增功能都要改原组件

const Button = ({ type, children }) => {
  // 每次新增按钮类型都要改这里
  if (type === 'save') {
    return <button style={{ background: 'blue' }}>{children}</button>;
  }
  if (type === 'delete') {
    return <button style={{ background: 'red' }}>{children}</button>;
  }
  // 要新增 'edit' 类型?又要改这里...
};
1
2
3
4
5
6
7
8
9
10

✅ 正确:通过配置扩展,不修改原组件

const Button = ({ style, children, ...props }) => (
  <button style={style} {...props}>
    {children}
  </button>
);

// 扩展:定义不同样式,不用改原组件
const SaveButton = ({ children, ...props }) => (
  <Button style={{ background: 'blue' }} {...props}>
    {children}
  </Button>
);

const DeleteButton = ({ children, ...props }) => (
  <Button style={{ background: 'red' }} {...props}>
    {children}
  </Button>
);

// 新增功能很简单,不用改原组件
const EditButton = ({ children, ...props }) => (
  <Button style={{ background: 'green' }} {...props}>
    {children}
  </Button>
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# L - 里氏替换原则 (Liskov Substitution Principle)

子组件应该能够替换其父组件而不破坏程序功能

❌ 错误:子组件改变了使用方式

// 普通输入框
const Input = ({ value, onChange }) => (
  <input value={value} onChange={onChange} />
);

// 数字输入框 - 改变了使用方式!
const NumberInput = ({ value, onChange }) => (
  <input 
    value={value} 
    onChange={(e) => onChange(Number(e.target.value))} // 这里改变了 onChange 的参数类型
  />
);

// 使用时会出问题
const Form = ({ InputComponent }) => {
  const [value, setValue] = useState('');
  
  return (
    <InputComponent 
      value={value} 
      onChange={setValue} // Input 传字符串,NumberInput 传数字,不一致!
    />
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

✅ 正确:保持一致的使用方式

// 基础输入框
const Input = ({ value, onChange }) => (
  <input value={value} onChange={onChange} />
);

// 数字输入框 - 保持相同的接口
const NumberInput = ({ value, onChange }) => (
  <input 
    type="number"
    value={value} 
    onChange={onChange} // 保持相同的参数类型
  />
);

// 现在可以安全替换
const Form = ({ InputComponent }) => {
  const [value, setValue] = useState('');
  
  return (
    <InputComponent 
      value={value} 
      onChange={(e) => setValue(e.target.value)} // 两个组件都能正常工作
    />
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# I - 接口隔离原则 (Interface Segregation Principle)

不应该强迫组件依赖它们不使用的接口

❌ 错误:传递整个大对象

const user = {
  id: 1,
  name: 'John',
  email: 'john@example.com',
  age: 25,
  address: '北京市朝阳区',
  phone: '13800138000',
  preferences: {...},
  permissions: {...}
};

// 只要显示名字,却要传整个用户对象
const UserName = ({ user }) => <span>{user.name}</span>;

// 只要显示头像,也要传整个用户对象
const UserAvatar = ({ user }) => <img src={user.avatar} />;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

✅ 正确:只传需要的数据

// 只传需要的 name
const UserName = ({ name }) => <span>{name}</span>;

// 只传需要的 avatar 和 name
const UserAvatar = ({ avatar, name }) => <img src={avatar} alt={name} />;

// 使用时按需传递
const UserCard = ({ user }) => (
  <div>
    <UserAvatar avatar={user.avatar} name={user.name} />
    <UserName name={user.name} />
  </div>
);
1
2
3
4
5
6
7
8
9
10
11
12
13

# D - 依赖反转原则 (Dependency Inversion Principle)

高层模块不应该依赖低层模块,两者都应该依赖抽象

❌ 错误:直接依赖具体的 API

const UserList = () => {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    // 直接写死了 fetch,难以测试和替换
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers);
  }, []);
  
  return (
    <div>
      {users.map(user => <div key={user.id}>{user.name}</div>)}
    </div>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

✅ 正确:通过参数传入具体实现

// 组件不关心数据怎么来的
const UserList = ({ getUsers }) => {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    getUsers().then(setUsers);
  }, [getUsers]);
  
  return (
    <div>
      {users.map(user => <div key={user.id}>{user.name}</div>)}
    </div>
  );
};

// 使用时传入具体实现
const App = () => {
  // 真实环境用真实 API
  const realGetUsers = () => fetch('/api/users').then(res => res.json());
  
  // 测试环境用假数据
  const mockGetUsers = () => Promise.resolve([
    { id: 1, name: 'John' },
    { id: 2, name: 'Jane' }
  ]);
  
  const getUsers = process.env.NODE_ENV === 'test' ? mockGetUsers : realGetUsers;
  
  return <UserList getUsers={getUsers} />;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 控制反转 IoC

控制反转是一种设计思想:把对象创建和依赖管理的控制权从组件本身转移给外部容器

  • 传统方式:我需要什么,我自己创建什么

  • 控制反转:我需要什么,别人给我什么

# IoC 的好处

  • 降低耦合度:组件不依赖具体实现

  • 提高可测试性:可以注入 mock 对象

// 测试时注入 mock 服务
const mockServices = {
  userService: {
    getUsers: () => Promise.resolve([{ id: 1, name: 'Test' }])
  }
};

render(
  <ServiceProvider services={mockServices}>
    <UserList />
  </ServiceProvider>
);
1
2
3
4
5
6
7
8
9
10
11
12
  • 增强可配置性:外部控制所有依赖
// 可以轻松切换不同环境的配置
const devConfig = { apiBase: 'http://localhost:3000' };
const prodConfig = { apiBase: 'https://api.prod.com' };

<ConfigProvider config={isDev ? devConfig : prodConfig}>
  <App />
</ConfigProvider>
1
2
3
4
5
6
7
  • 便于维护:依赖变更不影响组件
// 新增服务不需要修改组件
const enhancedServices = {
  ...originalServices,
  analyticsService: new AnalyticsService()
};

<ServiceProvider services={enhancedServices}>
  <App />
</ServiceProvider>
1
2
3
4
5
6
7
8
9

# 自定义 IoC

使用 InversifyJS、Acorn 和 Reflect(JavaScript 的反射 API)可以实现一个自定义的 IoC 容器。

1. InversifyJS

  • 作用:InversifyJS (opens new window) 是一个现成的 IoC 容器,提供依赖注入功能,通过装饰器(@injectable、@inject)和绑定机制管理依赖。

  • 在自定义 IoC 中的用途:可以作为参考或直接复用其容器逻辑(Container 类、绑定机制等),避免重复造轮子。如果要完全自定义,InversifyJS 的源码可以提供灵感(如依赖解析、生命周期管理)。

  • 局限:它是现成工具,直接使用可能限制定制化程度,但适合快速验证 IoC 概念。

2. Acorn

  • 作用:Acorn (opens new window) 是一个 JavaScript 解析器,能将代码解析为抽象语法树(AST),允许分析类、函数、装饰器等结构。

  • 在自定义 IoC 中的用途:通过解析源代码,提取类定义、构造函数参数或自定义装饰器信息,用于自动注册依赖或推断依赖关系。

  • 优势:支持静态代码分析,能处理动态注入场景(如根据文件结构自动注册服务)。

3. Reflect

  • 作用:Reflect (opens new window) 是 ECMAScript 的内置 API,提供运行时元数据操作(如 Reflect.getMetadata),常与 reflect-metadata (opens new window) 库结合使用,用于存储和读取类/方法的元数据(如依赖类型)。

  • 在自定义 IoC 中的用途:通过元数据存储依赖信息,动态解析构造函数参数,实现依赖注入的自动化。

  • 优势:与 TypeScript 配合良好,支持装饰器元数据的动态注入。

实现思路

1. 依赖注册

  • 使用 Acorn 解析 JavaScript/TypeScript 代码,识别类和装饰器(如 @injectable),提取构造函数参数。

  • 使用 Reflect 和 reflect-metadata 存储类或方法的元数据(如依赖的类型标识符)。

2. 容器实现

  • 仿照 InversifyJS 的 Container,实现一个简单的容器类,支持绑定(bind)和解析(resolve)。

  • 支持生命周期管理(如单例、瞬时)。

3. 依赖解析

  • 使用 Reflect 获取构造函数的元数据,解析依赖链。

  • 动态创建实例并注入依赖。

4. 自动加载(可选)

  • 使用 Acorn 扫描指定目录的代码文件,自动注册所有带有特定装饰器的类。

# 依赖注入 DI

依赖注入是一种依赖管理模式:不在组件内部创建依赖,而是从外部传入依赖,让组件专注于业务逻辑

依赖注入是控制反转的一种实现方式。

# DI 的好处

更容易测试、复用、维护、扩展。

  • 更好的测试性
// 测试时可以注入 mock 服务
const mockUserService = {
  getUsers: jest.fn().mockResolvedValue([
    { id: 1, name: 'Test User' }
  ])
};

render(<UserList userService={mockUserService} />);
1
2
3
4
5
6
7
8
  • 更高的灵活性
// 开发环境用 mock 数据
const devService = { getUsers: () => Promise.resolve(mockData) };

// 生产环境用真实 API
const prodService = { getUsers: () => fetch('/api/users').then(r => r.json()) };

const service = isDevelopment ? devService : prodService;
1
2
3
4
5
6
7
  • 更低的耦合度
// 组件不知道数据来源,只知道接口
const UserList = ({ dataSource }) => {
  // dataSource 可以是 API、localStorage、IndexedDB 等任何实现
};
1
2
3
4

# Awilix 和 Awilix-Koa

Awilix (opens new window) 是一个使用 TypeScript 编写的 JavaScript/Node 依赖注入容器库。它允许开发者通过 DI 模式构建可组合、可测试的软件,而无需特殊的注解或装饰器,从而将核心应用代码与 DI 机制解耦。

Awilix-Koa (opens new window) 是 Awilix 的官方 Koa 2 集成包,提供中间件、路由器和作用域管理工具,帮助在 Koa 应用中无缝使用 Awilix。它主要解决 Web 应用中的依赖注入问题,特别是每个 HTTP 请求创建一个独立的依赖作用域(scopePerRequest),避免状态共享。

# InversifyJS

InversifyJS (opens new window) 是一个轻量级、强大的依赖注入(Dependency Injection, DI)和控制反转(Inversion of Control, IoC)容器,专为 TypeScript 和 JavaScript 应用设计。它通过类构造函数识别和注入依赖,支持 SOLID 原则和最佳 OOP 实践,帮助开发者构建模块化、可测试的代码。

InversifyJS 是框架无关的,可与 Express、Hapi、React 等集成,支持 ES5/ES6+,并编译为纯 JavaScript 代码,可在浏览器、Node.js 或任何支持 ECMAScript 2022+ 的环境中运行。

核心优势:

  • 装饰器支持:使用 @injectable 和 @inject 装饰器简化注入,无需手动管理依赖。

  • 绑定类型:支持构造函数注入、标签(tags)、命名绑定和多重注入。

  • 生命周期管理:单例、瞬时等模式。

  • 测试友好:易于 mock 依赖,提高单元测试效率。

适用场景:

大型 Node.js/前端应用,如 API 服务、微服务或复杂组件架构。

# Awilix 和 InversifyJS 的区别

1. 依赖注入实现不同

InversifyJS:

  • 使用装饰器显式声明依赖,依赖 TypeScript 的元数据反射(reflect-metadata)。

  • 构造函数注入需明确指定依赖的标识符(如字符串或符号)。

import { Container, injectable, inject } from 'inversify';
import 'reflect-metadata';

@injectable()
class Katana {
  hit() {
		return 'Cutting with Katana!';
	}
}

@injectable()
class Ninja {
  constructor(@inject('Katana') private katana: Katana) {}
  fight() {
		return this.katana.hit();
	}
}

const container = new Container();
container.bind<Katana>('Katana').to(Katana).inSingletonScope();
container.bind<Ninja>('Ninja').to(Ninja);
const ninja = container.get<Ninja>('Ninja');
console.log(ninja.fight()); // 输出: Cutting with Katana!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Awilix:

  • 无需装饰器,通过容器注册函数(如 asClass、asFunction)定义依赖。

  • 支持代理模式(Proxy),自动解析构造函数参数,减少配置。

const awilix = require('awilix');

class Katana {
  hit() {
		return 'Cutting with Katana!';
	}
}

class Ninja {
  constructor({ katana }) { // 自动注入
    this.katana = katana;
  }
  fight() {
		return this.katana.hit();
	}
}

const container = awilix.createContainer();
container.register({
  katana: awilix.asClass(Katana).singleton(),
  ninja: awilix.asClass(Ninja).singleton()
});
const ninja = container.resolve('ninja');
console.log(ninja.fight()); // 输出: Cutting with Katana!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

2. 如何选择

  • 如果你使用 TypeScript,追求类型安全和企业级架构,选择 InversifyJS。

  • 如果你偏好 JavaScript 或轻量级开发,追求简单性和性能,选择 Awilix。

  • 如果项目基于 Koa 或 Express,Awilix 的内置集成会更省力。

  • 如果需要跨框架或复杂注入场景,InversifyJS 更灵活。

# NestJS

NestJS (opens new window) 内置了一个强大的依赖注入(Dependency Injection, DI)系统,基于 TypeScript 和 装饰器,深受 Angular 和 InversifyJS 的启发。NestJS 的 DI 系统是其核心特性之一,用于管理模块、服务、控制器等组件的依赖关系,简化代码组织,提高可测试性和模块化程度。

1. NestJS 依赖注入系统

  • 基于装饰器:NestJS 使用 TypeScript 的装饰器(如 @Injectable、@Inject)来标记可注入的服务或自定义提供者。

  • 模块化架构:依赖注入基于模块(@Module),每个模块可以定义提供者(providers)、控制器(controllers)和其他依赖。

  • 自动解析:NestJS 利用 TypeScript 的元数据(reflect-metadata)自动解析构造函数参数的类型,无需显式指定依赖标识符(除非使用自定义提供者)。

  • 作用域管理:

    • 单例(Singleton):默认作用域,同一实例在整个应用中共享。

    • 瞬时(Transient):每次解析生成新实例。

    • 请求作用域(Request-Scoped):为每个 HTTP 请求创建新实例,适合 Web 应用。

  • 内置容器:NestJS 内置 IoC 容器,管理所有依赖的注册和解析,无需额外引入第三方 DI 库。

  • 框架集成:与 Express 和 Fastify 无缝集成,支持 HTTP、WebSocket、微服务等场景。

# NestJS DI、InversifyJS 和 Awilix 的区别

  • NestJS DI:适合构建复杂的全栈应用或微服务,特别是有模块化需求(如 REST API、GraphQL、WebSocket)。如果你已经在用 NestJS,无需引入其他 DI 库。

  • InversifyJS:适合需要独立 DI 容器的场景,如非 Web 项目(CLI、React)或跨框架复用。

  • Awilix:适合快速开发的 Node.js 项目,特别是 Koa 或 Express 生态,注重简单性和性能。

# 面向切面编程 AOP

AOP 是一种编程思想:把横切关注点(Cross-cutting Concerns)从业务逻辑中分离出来,统一处理

  • 传统编程:每个函数都要写日志、错误处理、性能监控等

  • AOP 编程:把这些通用逻辑提取出来,自动 "织入" 到需要的地方

常见的横切关注点:

  • 🪵 日志记录:记录函数调用、参数、返回值

  • ⚠️ 错误处理:统一捕获和处理错误

  • ⏱️ 性能监控:测量函数执行时间

  • 🔐 权限验证:检查用户权限

  • 💾 缓存管理:缓存函数结果

  • 📊 数据埋点:用户行为统计

# AOP 的好处

  • 提高代码复用性

  • 减少代码重复

  • 增强可维护性

  • 保持业务逻辑纯净

# 使用装饰器实现 AOP

// 定义各种切面
const withLogging = (target, propertyName, descriptor) => {
  const originalMethod = descriptor.value;
  
  descriptor.value = async function(...args) {
    console.log(`📝 调用 ${propertyName},参数:`, args);
    
    const result = await originalMethod.apply(this, args);
    
    console.log(`📝 ${propertyName} 返回:`, result);
    return result;
  };
  
  return descriptor;
};

const withPerformance = (target, propertyName, descriptor) => {
  const originalMethod = descriptor.value;
  
  descriptor.value = async function(...args) {
    const startTime = Date.now();
    
    const result = await originalMethod.apply(this, args);
    
    console.log(`⏱️ ${propertyName} 耗时: ${Date.now() - startTime}ms`);
    return result;
  };
  
  return descriptor;
};

const withErrorHandling = (target, propertyName, descriptor) => {
  const originalMethod = descriptor.value;
  
  descriptor.value = async function(...args) {
    try {
      return await originalMethod.apply(this, args);
    } catch (error) {
      console.error(`${propertyName} 执行失败:`, error);
      throw error;
    }
  };
  
  return descriptor;
};

// 使用装饰器 - 业务逻辑非常清爽
class UserService {
  @withLogging
  @withPerformance
  @withErrorHandling
  async getUsers() {
    // 只关心业务逻辑
    const response = await fetch('/api/users');
    return response.json();
  }
  
  @withLogging
  @withPerformance
  @withErrorHandling
  async createUser(userData) {
    // 只关心业务逻辑
    const response = await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify(userData)
    });
    return response.json();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

# 使用高阶组件 (HOC) 实现 AOP

// 日志记录的 HOC
const withLogging = (WrappedComponent, componentName) => {
  return (props) => {
    console.log(`🔍 渲染 ${componentName},props:`, props);
    
    return <WrappedComponent {...props} />;
  };
};

// 性能监控的 HOC
const withPerformance = (WrappedComponent, componentName) => {
  return (props) => {
    const renderStart = Date.now();
    
    useEffect(() => {
      console.log(`${componentName} 渲染耗时: ${Date.now() - renderStart}ms`);
    });
    
    return <WrappedComponent {...props} />;
  };
};

// 错误边界的 HOC
const withErrorBoundary = (WrappedComponent) => {
  class ErrorBoundary extends Component {
    constructor(props) {
      super(props);
      this.state = { hasError: false };
    }
    
    static getDerivedStateFromError(error) {
      return { hasError: true };
    }
    
    componentDidCatch(error, errorInfo) {
      console.error('🚨 组件错误:', error, errorInfo);
    }
    
    render() {
      if (this.state.hasError) {
        return <div>组件出错了</div>;
      }
      
      return <WrappedComponent {...this.props} />;
    }
  }
  
  return ErrorBoundary;
};

// 业务组件 - 只关心业务逻辑
const UserList = ({ users }) => (
  <div>
    {users.map(user => (
      <div key={user.id}>{user.name}</div>
    ))}
  </div>
);

// 使用多个切面增强组件
const EnhancedUserList = withErrorBoundary(
  withPerformance(
    withLogging(UserList, 'UserList'), 
    'UserList'
  )
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

# 使用自定义 Hook 实现 AOP

// 网络请求的 AOP Hook
const useApiCall = (apiFunc, options = {}) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const execute = useCallback(async (...args) => {
    // 日志切面
    console.log('🌐 开始 API 调用:', apiFunc.name, args);
    
    // 性能切面
    const startTime = Date.now();
    
    try {
      setLoading(true);
      setError(null);
      
      // 执行实际的 API 调用
      const result = await apiFunc(...args);
      
      setData(result);
      
      // 性能日志
      console.log(`⏱️ API 调用耗时: ${Date.now() - startTime}ms`);
      
      // 成功日志
      console.log('✅ API 调用成功:', result);
      
      return result;
    } catch (err) {
      // 错误处理切面
      console.error('❌ API 调用失败:', err);
      setError(err);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [apiFunc]);
  
  return {
    data,
    loading,
    error,
    execute
  };
};

// 使用 AOP Hook
const UserManagement = () => {
  const getUsersCall = useApiCall(
    async () => {
      const response = await fetch('/api/users');
      return response.json();
    }
  );
  
  const createUserCall = useApiCall(
    async (userData) => {
      const response = await fetch('/api/users', {
        method: 'POST',
        body: JSON.stringify(userData)
      });
      return response.json();
    }
  );
  
  return (
    <div>
      <button onClick={getUsersCall.execute}>
        {getUsersCall.loading ? '加载中...' : '获取用户'}
      </button>
      
      {getUsersCall.error && (
        <div>错误: {getUsersCall.error.message}</div>
      )}
      
      {getUsersCall.data && (
        <div>用户数量: {getUsersCall.data.length}</div>
      )}
    </div>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
上次更新时间: 2025年09月24日 23:25:32