Skip to content

Commit ef3357a

Browse files
committed
refactor: enhance Vue 3 composables and improve React components with new mapping features
1 parent fa58ef5 commit ef3357a

36 files changed

Lines changed: 1174 additions & 778 deletions

README.md

Lines changed: 54 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<div align="center">
44

5-
**轻量级、类型安全的 TypeScript 对象映射库**
5+
**TypeScript 对象映射库**
66

77
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
88
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
@@ -12,316 +12,127 @@
1212

1313
</div>
1414

15-
## 📖 简介
15+
## 简介
1616

17-
**Orika-JS** 是一个专为 TypeScript 设计的对象映射库,灵感来自 Java 的 Orika 框架。它帮助你在分层架构中优雅地处理不同对象模型之间的转换(PO/DO/DTO/VO)
17+
Orika-JS 是一个类型安全的对象映射库,用于简化不同数据模型之间的转换(Entity/DTO/VO)。灵感来自 Java 的 Orika 框架。
1818

19-
### 为什么需要对象映射?
19+
## 特性
2020

21-
现代软件架构中,分层设计是最佳实践。不同层级使用不同的对象模型:
21+
- **类型安全** - 完整的 TypeScript 类型推导和编译时检查
22+
- **自动映射** - 同名字段自动映射,零配置即可使用
23+
- **异步支持** - 支持异步转换器和并发控制
24+
- **框架适配** - 提供 Vue 3 / React 适配器
25+
- **轻量级** - 零运行时依赖,支持 Tree-shaking
2226

23-
```
24-
┌─────────────────┬────────────────────┬───────────────────┐
25-
│ 表现层 (API) │ 业务层 (Service) │ 持久层 (DB) │
26-
├─────────────────┼────────────────────┼───────────────────┤
27-
│ DTO/VO │ DO/BO │ PO/Entity │
28-
└─────────────────┴────────────────────┴───────────────────┘
29-
↓ ↓ ↓
30-
需要转换 需要转换 需要转换
31-
```
32-
33-
传统的手写转换代码存在诸多问题:
34-
- ❌ 大量重复的样板代码
35-
- ❌ 字段遗漏导致的运行时错误
36-
- ❌ 模型变更后需要同步修改多处
37-
- ❌ 缺乏类型安全保障
38-
39-
**Orika-JS 采用声明式配置,一次定义,全局复用:**
40-
- ✅ 完整的 TypeScript 类型推导
41-
- ✅ 约定优于配置(同名字段自动映射)
42-
- ✅ 支持字段重命名、嵌套对象、自定义转换
43-
- ✅ 框架集成(Vue 3 / React)
44-
45-
## ✨ 核心特性
46-
47-
| 特性 | 说明 |
48-
|------|------|
49-
| 🔒 **类型安全** | 完整的 TypeScript 泛型支持,编译时类型检查 |
50-
| 🎯 **约定优于配置** | 同名字段自动映射,零配置即可使用 |
51-
| ⚡️ **高性能** | 映射缓存、惰性求值、批量处理优化 |
52-
| 🔄 **异步支持** | 原生支持异步转换器和并发控制 |
53-
| 🎨 **灵活配置** | 字段重命名、条件映射、自定义转换器 |
54-
| 🚀 **框架集成** | Vue 3 响应式 / React Hooks |
55-
| 📦 **零依赖** | 核心库无运行时依赖,支持 Tree-shaking |
56-
57-
## 📦 安装
27+
## 安装
5828

5929
```bash
60-
# 核心库(必需)
30+
# 核心库
6131
npm install @orika-js/core
6232

63-
# Vue 3 项目
33+
# Vue 3
6434
npm install @orika-js/vue3
6535

66-
# React 项目
36+
# React
6737
npm install @orika-js/react
6838
```
6939

70-
## 🚀 快速开始
71-
72-
### 基础用法
73-
74-
**3 步完成对象映射:**
40+
## 快速开始
7541

7642
```typescript
7743
import { createMapperBuilder, MapperFactory } from '@orika-js/core';
7844

79-
// 1️⃣ 定义模型
45+
// 定义模型
8046
class UserEntity {
8147
id: number;
8248
username: string;
8349
password: string;
8450
email: string;
85-
createdAt: Date;
8651
}
8752

8853
class UserDTO {
8954
id: number;
90-
displayName: string; // 字段重命名
91-
email: string; // 同名字段自动映射
55+
displayName: string;
56+
email: string;
9257
}
9358

94-
// 2️⃣ 配置映射(只需配置一次)
59+
// 配置映射
9560
createMapperBuilder<UserEntity, UserDTO>()
9661
.from(UserEntity).to(UserDTO)
97-
.mapField('username', 'displayName') // 字段重命名
98-
.exclude('password', 'createdAt') // 排除敏感字段
62+
.mapField('username', 'displayName')
63+
.exclude('password')
9964
.register();
10065

101-
// 3️⃣ 执行映射
66+
// 执行映射
10267
const factory = MapperFactory.getInstance();
103-
const entity = {
104-
id: 1,
105-
username: 'Alice',
106-
password: 'secret',
107-
email: 'alice@example.com',
108-
createdAt: new Date()
109-
};
110-
111-
const dto = factory.map(entity, UserEntity, UserDTO);
112-
// 结果: { id: 1, displayName: 'Alice', email: 'alice@example.com' }
113-
```
68+
const dto = factory.map(userEntity, UserEntity, UserDTO);
11469

115-
### 高级特性
70+
// 批量映射
71+
const dtos = factory.mapArray(users, UserEntity, UserDTO);
11672

117-
```typescript
118-
// 自定义转换逻辑
73+
// 自定义转换
11974
createMapperBuilder<User, UserDTO>()
12075
.from(User).to(UserDTO)
121-
.forMember('age', (src) => 2024 - src.birthYear)
122-
.forMember('fullName', (src) => `${src.firstName} ${src.lastName}`)
123-
.register();
124-
125-
// 异步转换(如需要查询数据库)
126-
createMapperBuilder<Post, PostDTO>()
127-
.from(Post).to(PostDTO)
128-
.forMemberAsync('author', async (src) => {
129-
return await fetchUser(src.authorId);
130-
})
76+
.forMember('age', (src) => new Date().getFullYear() - src.birthYear)
77+
.forMemberAsync('author', async (src) => await fetchUser(src.authorId))
13178
.register();
132-
133-
// 批量映射
134-
const dtos = factory.mapArray(users, User, UserDTO);
135-
136-
// 双向映射
137-
const { toB, toA } = factory.bidirectional(UserEntity, UserDTO);
138-
const dto = toB(entity);
139-
const entity2 = toA(dto);
14079
```
14180

142-
## 🎨 框架集成
81+
## 框架集成
14382

14483
### Vue 3
14584

146-
`@orika-js/vue3` 提供完整的 Vue 3 响应式系统集成:
147-
14885
```typescript
149-
import { useMapper, mapToReactive, mapToComputed } from '@orika-js/vue3';
86+
import { useMapper, mapToReactive, mapToComputed, createPiniaMapperPlugin } from '@orika-js/vue3';
15087

151-
// Composition API
15288
const { map, mapArray } = useMapper(UserEntity, UserDTO);
153-
const userDTO = map(userEntity);
154-
155-
// 响应式映射
15689
const reactiveDTO = mapToReactive(user, User, UserDTO);
90+
const computedDTO = mapToComputed(userRef, User, UserDTO);
15791

158-
// 计算属性(自动追踪依赖)
159-
const userRef = ref(user);
160-
const userDTO = mapToComputed(userRef, User, UserDTO);
161-
```
162-
163-
**Pinia Store 集成:**
164-
165-
```typescript
166-
import { createPiniaMapperPlugin } from '@orika-js/vue3';
167-
168-
const pinia = createPinia();
92+
// Pinia
16993
pinia.use(createPiniaMapperPlugin());
170-
171-
// 在 Store 中使用
172-
export const useUserStore = defineStore('user', () => {
173-
const users = ref([]);
174-
175-
async function fetchUsers() {
176-
const data = await api.getUsers();
177-
users.value = this.$mapper.mapArray(data, UserEntity, UserDTO);
178-
}
179-
180-
return { users, fetchUsers };
181-
});
94+
const users = this.$mapper.mapArray(data, UserEntity, UserDTO);
18295
```
18396

184-
📚 [查看 Vue 3 完整文档](./packages/vue3)
97+
[完整文档](./packages/vue3) · [示例](./examples/vue3-app)
18598

18699
### React
187100

188-
`@orika-js/react` 提供全面的 Hooks、组件和 HOC:
189-
190101
```typescript
191-
import { useMapper, useMemoizedMapper, MapperProvider } from '@orika-js/react';
192-
193-
function App() {
194-
return (
195-
<MapperProvider>
196-
<UserProfile />
197-
</MapperProvider>
198-
);
199-
}
200-
201-
function UserProfile() {
202-
const [user, setUser] = useState(userEntity);
203-
204-
// 基础映射 Hook
205-
const { map } = useMapper(UserEntity, UserDTO);
206-
const dto = map(user);
207-
208-
// 记忆化映射(自动缓存)
209-
const memoizedDTO = useMemoizedMapper(user, UserEntity, UserDTO);
210-
211-
return <div>{dto.displayName}</div>;
212-
}
213-
```
102+
import { useMapper, useMemoizedMapper, MapperProvider, Mapper, withMapper } from '@orika-js/react';
214103

215-
**声明式组件:**
104+
const { map } = useMapper(UserEntity, UserDTO);
105+
const dto = useMemoizedMapper(user, UserEntity, UserDTO);
216106

217-
```tsx
107+
// 组件
218108
<Mapper source={user} sourceClass={UserEntity} destClass={UserDTO}>
219-
{(dto, isMapping, error) => (
220-
error ? <ErrorDisplay /> :
221-
isMapping ? <Loading /> :
222-
<UserProfile data={dto} />
223-
)}
109+
{(dto) => <div>{dto.displayName}</div>}
224110
</Mapper>
225-
```
226-
227-
**HOC 模式:**
228-
229-
```typescript
230-
const UserProfileWithMapper = withMapper({
231-
sourceClass: UserEntity,
232-
destClass: UserDTO,
233-
sourceProp: 'user',
234-
destProp: 'userDTO'
235-
})(UserProfile);
236-
```
237-
238-
📚 [查看 React 完整文档](./packages/react)
239-
240-
## 📚 包说明
241-
242-
|| 版本 | 说明 |
243-
|---|------|------|
244-
| [@orika-js/core](./packages/core) | ![npm](https://img.shields.io/npm/v/@orika-js/core) | 核心映射引擎,零依赖 |
245-
| [@orika-js/vue3](./packages/vue3) | ![npm](https://img.shields.io/npm/v/@orika-js/vue3) | Vue 3 适配器,支持响应式和 Pinia |
246-
| [@orika-js/react](./packages/react) | ![npm](https://img.shields.io/npm/v/@orika-js/react) | React 适配器,提供 Hooks 和组件 |
247-
248-
## 🎯 实际应用场景
249-
250-
### 场景 1: API 数据转换
251-
252-
```typescript
253-
// API 响应 → DTO → 前端展示
254-
async function fetchUsers() {
255-
const response = await fetch('/api/users');
256-
const rawData = await response.json();
257-
258-
// 自动排除敏感字段、格式化日期
259-
return factory.mapArray(rawData, UserEntity, UserDTO);
260-
}
261-
```
262111

263-
### 场景 2: 表单提交
264-
265-
```typescript
266-
// 表单数据 → 请求对象 → API
267-
function submitForm(formData: UserFormData) {
268-
const request = factory.map(formData, UserFormData, CreateUserRequest);
269-
return api.createUser(request);
270-
}
112+
// HOC
113+
const Enhanced = withMapper({ sourceClass: UserEntity, destClass: UserDTO })(Component);
271114
```
272115

273-
### 场景 3: 分层架构
116+
[完整文档](./packages/react) · [示例](./examples/react-demo)
274117

275-
```
276-
Controller (DTO) → Service (DO) → Repository (PO) → Database
277-
↓ ↓ ↓
278-
用户请求 业务逻辑 数据持久化
279-
```
118+
## 包说明
280119

281-
每一层都使用适合的对象模型,通过 Orika-JS 自动转换。
120+
|| 说明 |
121+
|---|------|
122+
| [@orika-js/core](./packages/core) | 核心映射引擎,零依赖 |
123+
| [@orika-js/vue3](./packages/vue3) | Vue 3 适配器 |
124+
| [@orika-js/react](./packages/react) | React 适配器 |
282125

283-
## 🛠 开发
126+
## 开发
284127

285128
```bash
286-
# 安装依赖
287-
pnpm install
288-
289-
# 构建所有包
290-
pnpm build
291-
292-
# 开发模式(监听文件变化)
293-
pnpm dev
294-
295-
# 运行示例
296-
cd examples/vue3-app && pnpm dev
297-
cd examples/react-demo && pnpm dev
129+
pnpm install # 安装
130+
pnpm build # 构建
131+
pnpm dev # 开发
298132
```
299133

300-
## 📖 示例
301-
302-
查看 [examples](./examples) 目录获取完整示例:
303-
304-
- **基础示例**
305-
- `01-basic.ts` - 基础映射
306-
- `02-async.ts` - 异步映射
307-
- `03-collections.ts` - 集合映射
308-
- `04-validation.ts` - 数据验证
309-
- `05-advanced.ts` - 高级特性
310-
311-
- **框架集成**
312-
- `vue3-app/` - Vue 3 完整应用示例
313-
- `react-demo/` - React 应用示例
314-
315-
## 🤝 贡献
316-
317-
欢迎提交 Issue 和 Pull Request!
318-
319-
## 📄 许可证
320-
321-
[MIT](./LICENSE) © [Steven Lee](https://github.com/stevenleep)
134+
查看 [examples](./examples) 目录获取更多示例。
322135

323-
## 🔗 链接
136+
## 许可证
324137

325-
- [GitHub 仓库](https://github.com/stevenleep/orika-js)
326-
- [问题反馈](https://github.com/stevenleep/orika-js/issues)
327-
- [变更日志](./CHANGELOG.md)
138+
[MIT](./LICENSE) · [GitHub](https://github.com/stevenleep/orika-js) · [Issues](https://github.com/stevenleep/orika-js/issues)

0 commit comments

Comments
 (0)