Files
rn-app/docs/LIBRARIES.md
2025-11-05 17:24:55 +08:00

390 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📚 常用工具库使用指南
本文档介绍项目中已安装的常用工具库及其使用方法。
## 📦 已安装的库列表
### 工具类库
| 库名 | 版本 | 用途 |
| ------------------- | -------- | ---------------------------------------- |
| **lodash-es** | ^4.17.21 | JavaScript 工具函数库ES modules 版本) |
| **dayjs** | ^1.11.19 | 轻量级日期处理库 |
| **axios** | ^1.13.1 | HTTP 请求库 |
| **zustand** | ^5.0.8 | 轻量级状态管理 |
| **react-hook-form** | ^7.66.0 | 表单处理库 |
| **zod** | ^4.1.12 | TypeScript 数据验证库 |
### Expo 原生模块
| 库名 | 版本 | 用途 |
| --------------------------------------------- | ------- | -------------- |
| **@react-native-async-storage/async-storage** | ^2.2.0 | 本地存储 |
| **expo-image** | ^3.0.10 | 优化的图片组件 |
| **expo-haptics** | ^15.0.7 | 触觉反馈 |
### 开发工具
| 库名 | 版本 | 用途 |
| -------------------- | -------- | ----------------------------- |
| **@types/lodash-es** | ^4.17.12 | Lodash-ES TypeScript 类型定义 |
---
## 🚀 快速开始
### 1. Lodash-ES - JavaScript 工具库
> **注意**:使用 `lodash-es` 而不是 `lodash`,支持 ES modules 和更好的 tree-shaking。
```typescript
// 推荐按需导入tree-shaking 友好)
import { map, filter, uniq, pick, cloneDeep, debounce } from 'lodash-es';
// 数组操作
map([1, 2, 3], (n) => n * 2); // [2, 4, 6]
filter([1, 2, 3, 4], (n) => n % 2 === 0); // [2, 4]
uniq([1, 2, 2, 3]); // [1, 2, 3]
// 对象操作
pick({ a: 1, b: 2, c: 3 }, ['a', 'b']); // { a: 1, b: 2 }
cloneDeep(obj); // 深拷贝
// 防抖和节流
const handleSearch = debounce((text) => {
console.log('Searching:', text);
}, 300);
// 也可以全量导入(不推荐,会增加包体积)
import _ from 'lodash-es';
_.map([1, 2, 3], (n) => n * 2);
```
### 2. Day.js - 日期处理
```typescript
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/zh-cn';
dayjs.extend(relativeTime);
dayjs.locale('zh-cn');
// 格式化
dayjs().format('YYYY-MM-DD HH:mm:ss');
// 相对时间
dayjs().fromNow(); // '几秒前'
dayjs().subtract(1, 'day').fromNow(); // '1天前'
// 日期操作
dayjs().add(7, 'day'); // 7天后
dayjs().startOf('month'); // 本月第一天
```
### 3. Axios - HTTP 请求
```typescript
import axios from 'axios';
// 创建实例
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
// 请求拦截器
api.interceptors.request.use(async (config) => {
const token = await AsyncStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 使用
const data = await api.get('/users');
await api.post('/users', { name: 'John' });
```
### 4. Zustand - 状态管理
```typescript
import { create } from 'zustand';
interface UserState {
user: User | null;
setUser: (user: User) => void;
}
export const useUserStore = create<UserState>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
// 在组件中使用
const user = useUserStore((state) => state.user);
const setUser = useUserStore((state) => state.setUser);
```
### 5. React Hook Form + Zod - 表单处理
```typescript
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email('请输入有效的邮箱'),
password: z.string().min(6, '密码至少6个字符'),
});
type FormData = z.infer<typeof schema>;
function LoginForm() {
const { control, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => {
console.log(data);
};
return (
<Controller
control={control}
name="email"
render={({ field: { onChange, value } }) => (
<TextInput
value={value}
onChangeText={onChange}
placeholder="邮箱"
/>
)}
/>
);
}
```
### 6. AsyncStorage - 本地存储
```typescript
import AsyncStorage from '@react-native-async-storage/async-storage';
// 存储
await AsyncStorage.setItem('key', JSON.stringify(value));
// 读取
const value = await AsyncStorage.getItem('key');
const data = value ? JSON.parse(value) : null;
// 删除
await AsyncStorage.removeItem('key');
// 清空
await AsyncStorage.clear();
```
### 7. Expo Image - 优化的图片组件
```typescript
import { Image } from 'expo-image';
<Image
source={{ uri: 'https://example.com/image.jpg' }}
placeholder={require('@/assets/placeholder.png')}
contentFit="cover"
transition={1000}
style={{ width: 300, height: 300 }}
/>
```
### 8. Expo Haptics - 触觉反馈
```typescript
import * as Haptics from 'expo-haptics';
// 轻触反馈
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
// 成功反馈
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
// 错误反馈
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
```
---
## 💡 实用示例
### 创建 API 工具类
```typescript
// utils/api.ts
import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
api.interceptors.request.use(async (config) => {
const token = await AsyncStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
api.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
AsyncStorage.removeItem('token');
}
return Promise.reject(error);
}
);
export default api;
```
### 创建 Storage 工具类
```typescript
// utils/storage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
class Storage {
static async setObject<T>(key: string, value: T): Promise<void> {
await AsyncStorage.setItem(key, JSON.stringify(value));
}
static async getObject<T>(key: string): Promise<T | null> {
const value = await AsyncStorage.getItem(key);
return value ? JSON.parse(value) : null;
}
static async remove(key: string): Promise<void> {
await AsyncStorage.removeItem(key);
}
}
export default Storage;
```
### 创建用户状态管理
```typescript
// stores/userStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
interface User {
id: string;
name: string;
email: string;
}
interface UserState {
user: User | null;
setUser: (user: User) => void;
clearUser: () => void;
}
export const useUserStore = create<UserState>()(
persist(
(set) => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
}),
{
name: 'user-storage',
storage: createJSONStorage(() => AsyncStorage),
}
)
);
```
### 日期格式化工具
```typescript
// utils/date.ts
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/zh-cn';
dayjs.extend(relativeTime);
dayjs.locale('zh-cn');
export const formatDate = (date: Date | string | number) => {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss');
};
export const formatRelativeTime = (date: Date | string | number) => {
return dayjs(date).fromNow();
};
export const formatChatTime = (timestamp: number) => {
const date = dayjs(timestamp);
const now = dayjs();
if (date.isSame(now, 'day')) {
return date.format('HH:mm');
} else if (date.isSame(now.subtract(1, 'day'), 'day')) {
return '昨天 ' + date.format('HH:mm');
} else if (date.isSame(now, 'year')) {
return date.format('MM-DD HH:mm');
} else {
return date.format('YYYY-MM-DD');
}
};
```
---
## 📝 最佳实践
1. **代码组织**
- 将 API 配置放在 `utils/api.ts`
- 将存储工具放在 `utils/storage.ts`
- 将状态管理放在 `stores/` 目录
- 将验证规则放在 `schemas/` 目录
2. **性能优化**
- 使用 `_.debounce``_.throttle` 优化频繁触发的事件
- 使用 Zustand 的选择器避免不必要的重渲染
- 使用 Expo Image 的缓存策略
3. **类型安全**
- 使用 Zod 定义数据结构并自动生成 TypeScript 类型
- 为 Zustand store 定义完整的类型
- 使用 `@types/lodash` 获得完整的类型提示
4. **错误处理**
- 在 Axios 拦截器中统一处理错误
- 使用 try-catch 包裹异步操作
- 提供用户友好的错误提示
---
## 🔗 相关资源
- [Lodash 文档](https://lodash.com/docs/)
- [Day.js 文档](https://day.js.org/)
- [Axios 文档](https://axios-http.com/)
- [Zustand 文档](https://zustand-demo.pmnd.rs/)
- [React Hook Form 文档](https://react-hook-form.com/)
- [Zod 文档](https://zod.dev/)
- [AsyncStorage 文档](https://react-native-async-storage.github.io/async-storage/)
- [Expo Image 文档](https://docs.expo.dev/versions/latest/sdk/image/)
- [Expo Haptics 文档](https://docs.expo.dev/versions/latest/sdk/haptics/)
---
**提示**:所有库都已安装并配置好,可以直接在项目中使用!🎉