13 KiB
13 KiB
Duooomi Expo App - Copilot Instructions
项目概述
这是一个基于 Expo + React Native 的跨平台移动应用,使用 TypeScript 开发,集成了蓝牙设备通信、模板生成、用户认证等核心功能。项目采用 Monorepo 架构,使用 Bun 作为包管理器。
技术栈
核心框架
- Expo SDK 54 - 跨平台开发框架
- React 19.1.0 - UI 框架
- React Native 0.81.5 - 原生渲染
- Expo Router - 文件系统路由
- TypeScript 5.9 - 类型安全
状态管理与数据
- MobX 6 + mobx-react-lite - 响应式状态管理
- Better Auth - 身份认证
- AsyncStorage - 本地存储
UI 与样式
- NativeWind 4 - Tailwind CSS for React Native
- Tailwind CSS 3.4 - 原子化 CSS
- tailwind-variants - 变体管理
- class-variance-authority - CVA 工具
- Expo Linear Gradient - 渐变效果
蓝牙与设备
- react-native-ble-plx - BLE 中心设备
- react-native-multi-ble-peripheral - BLE 外设
- 自定义协议层 (
ble/protocol/)
媒体与资源
- Expo Image - 图片处理
- Expo Image Picker - 相册选择
- react-native-video - 视频播放
- Expo Media Library - 媒体库访问
支付与内购
- Expo IAP - 应用内购买
- Stripe - 支付集成
- Expo Native Alipay / WeChat - 第三方支付
开发工具
- ESLint 9 - 代码检查
- Prettier - 代码格式化
- Sentry - 错误监控
- EAS Build & Update - 云构建与 OTA 更新
项目结构
expo-duooomi-app/
├── app/ # Expo Router 路由页面
│ ├── (tabs)/ # 主 Tab 导航
│ │ ├── index.tsx # 首页(模板列表)
│ │ ├── explore.tsx # 探索页(测试功能)
│ │ ├── generate.tsx # 生成页(AI 生成)
│ │ └── sync.tsx # 同步页(设备同步)
│ ├── _layout.tsx # 根布局
│ ├── auth.tsx # 登录页
│ ├── service.tsx # 服务条款
│ └── pointList.tsx # 积分充值
├── components/ # 通用 UI 组件
│ ├── ui/ # UI 基础组件
│ ├── themed-*/ # 主题化组件
│ └── BannerSection.tsx # 业务组件
├── hooks/ # React Hooks
│ ├── actions/ # 业务操作 hooks
│ ├── core/ # 核心 hooks
│ └── data/ # 数据获取 hooks
├── stores/ # MobX 状态管理
│ ├── bleStore.ts # 蓝牙状态
│ ├── userStore.ts # 用户状态
│ └── userBalanceStore.ts # 用户余额
├── ble/ # 蓝牙模块
│ ├── protocol/ # 协议定义
│ │ ├── types.ts # 类型定义(Zod)
│ │ └── utils.ts # 协议工具
│ ├── managers/ # 管理器
│ │ └── bleManager.ts # BLE 中心管理
│ └── flows/ # 业务流程
├── utils/ # 工具函数
│ ├── cn.ts # className 工具
│ ├── uploadFile.ts # 文件上传
│ └── storage.ts # 本地存储
├── lib/ # 第三方库封装
│ └── auth.ts # Better Auth 客户端
├── constants/ # 常量定义
├── @share/ # Monorepo 共享模块
│ ├── components/ # 共享组件
│ ├── hooks/ # 共享 hooks
│ └── apis/ # API 封装
├── assets/ # 静态资源
├── scripts/ # 构建脚本
│ └── export-icp-source.ts # ICP 备案导出
├── .env.development # 开发环境变量
├── .env.test # 测试环境变量
├── .env.production # 生产环境变量
├── eas.json # EAS 构建配置
├── app.config.js # Expo 应用配置
└── AGENTS.md # AI Agent 协作规范
代码规范
命名约定
文件命名
- 路由页面:
kebab-case.tsx或camelCase.tsx(如pointList.tsx,point-list.tsx) - 组件文件:
PascalCase.tsx(如BannerSection.tsx) - Hooks:
camelCase.ts或kebab-case.ts(如useTemplates.ts,use-template-actions.ts) - 工具函数:
camelCase.ts(如uploadFile.ts) - 类型文件:
types.ts - 常量文件:
camelCase.ts
变量与函数命名
- 组件:
PascalCase(BannerSection,ThemedText) - Hook 函数:
camelCase以use开头 (useTemplates,useUserBalance) - 普通函数:
camelCase(uploadFile,cn) - 常量:
SCREAMING_SNAKE_CASE(BLE_UUIDS,APP_VERSION) - 枚举/对象常量:
SCREAMING_SNAKE_CASE(PREPARE_TRANSFER_STATUS) - 类型/接口:
PascalCase(Template,MediaItem,SourceLine) - Store 实例:
camelCase(bleStore,userStore)
TypeScript 规范
类型定义
// 使用 Zod 进行运行时校验
import { z } from 'zod'
export const PrepareTransferRequestZod = z.object({
type: z.number().describe('命令类型 0x14'),
key: z.string().describe('资源 key'),
size: z.number().describe('文件大小(bytes)'),
})
export type PrepareTransferRequest = z.infer<typeof PrepareTransferRequestZod>
// 接口定义
interface SourceLine {
content: string
filePath: string
lineNumber: number
isFileHeader: boolean
isFileEnd: boolean
}
// 类型别名
type ActiveTab = 'gen' | '' | 'new' | 'like'
严格模式
- 启用
strict: true - 避免使用
any,优先使用unknown或明确类型 - 使用可选链 (
?.) 和空值合并 (??)
样式规范
Tailwind CSS / NativeWind
// 使用 className 而非内联样式
<Block className="flex-row items-center justify-between px-4 py-2">
<Text className="text-sm font-bold text-white">标题</Text>
</Block>
// 使用 cn() 工具合并条件样式
import { cn } from '@/utils/cn'
<Block className={cn(
"border-2 bg-white p-4",
isSelected && "border-accent",
isDisabled && "opacity-50"
)}>
避免内联样式
- 不推荐:
style={{ width: 100, height: 100 }} - 推荐:
className="w-[100px] h-[100px]" - 例外: 动态计算的值可以使用 style (如
style={{ width: itemWidth }})
React 规范
组件定义
// 函数组件 + TypeScript
import { observer } from 'mobx-react-lite'
import React from 'react'
type Props = {
title: string
onPress?: () => void
isActive?: boolean
}
export const MyComponent = observer(function MyComponent({
title,
onPress,
isActive = false
}: Props) {
return (
<Block onPress={onPress}>
<Text>{title}</Text>
</Block>
)
})
Hooks 使用
// 自定义 Hook
export function useTemplates() {
const [data, setData] = useState<Template[]>([])
const [loading, setLoading] = useState(false)
const execute = useCallback(async () => {
setLoading(true)
try {
const result = await api.getTemplates()
setData(result)
} finally {
setLoading(false)
}
}, [])
return { data, loading, execute }
}
MobX 规范
// Store 定义
import { makeAutoObservable, runInAction } from 'mobx'
class UserStore {
user: User | null = null
isLogin = false
constructor() {
makeAutoObservable(this)
}
async login(credentials: Credentials) {
const user = await authApi.login(credentials)
runInAction(() => {
this.user = user
this.isLogin = true
})
}
}
export const userStore = new UserStore()
// 组件使用
import { observer } from 'mobx-react-lite'
export const Profile = observer(function Profile() {
const { user, isLogin } = userStore
if (!isLogin) return <Login />
return <Text>{user.name}</Text>
})
导入规范
// 使用 @ 别名
import { Block, Text } from '@/@share/components'
import { useTemplates } from '@/hooks/data'
import { userStore } from '@/stores'
import { cn } from '@/utils/cn'
// 按类型分组排序(自动由 eslint-plugin-simple-import-sort 处理)
// 1. React 相关
// 2. 第三方库
// 3. 绝对路径导入 (@/)
// 4. 相对路径导入 (./)
// 5. 类型导入
组件库使用
// 使用 @share/components 中的组件,而非 react-native 原生组件
// ✅ 推荐
import { Block, Text, Img } from '@/@share/components'
// ❌ 避免
import { View, Text, Image } from 'react-native'
// Block 替代 View
<Block className="flex-1 bg-white">
<Text>内容</Text>
</Block>
// Toast 统一使用项目封装的版本
import { Toast } from '@/@share/components'
Toast.show({ title: '操作成功' })
Toast.showLoading({ title: '加载中...' })
Toast.hideLoading()
Toast.showModal(<CustomModal />)
Toast.hideModal()
错误处理
// 统一错误处理
try {
await api.uploadFile(file)
Toast.show({ title: '上传成功' })
} catch (error) {
console.error('上传失败:', error)
Toast.show({ title: error instanceof Error ? error.message : '上传失败' })
}
// Sentry 错误上报
import * as Sentry from '@sentry/react-native'
try {
// ...
} catch (error) {
Sentry.captureException(error)
throw error
}
最佳实践
组件设计
- 单一职责: 每个组件只做一件事
- Props 设计: 明确的 Props 类型,合理的默认值
- 记忆化: 使用
memo,useMemo,useCallback优化性能 - 条件渲染: 提前返回,减少嵌套
// 提前返回
if (!isLogin) return <Login />
if (loading) return <Loading />
return <Content />
性能优化
// 使用 FlashList 代替 FlatList
import { FlashList } from '@shopify/flash-list'
<FlashList
data={items}
renderItem={({ item }) => <Item data={item} />}
estimatedItemSize={100}
/>
// 图片优化
import { Img } from '@/@share/components'
<Img
src={uri}
className="w-full h-full"
isCompression
/>
蓝牙通信
// 使用 bleManager 管理 BLE 连接
import { bleManager } from '@/ble/managers/bleManager'
// 扫描设备
await bleManager.startScan()
// 连接设备
await bleManager.connectToDevice(device)
// 发送数据
await bleManager.transferMediaSingle(fileUrl)
// 断开连接
await bleManager.disconnectDevice()
状态管理
- 本地 UI 状态: 使用
useState - 跨组件状态: 使用 MobX Store
- 服务端状态: 使用自定义 Hook (如
useTemplates) - 持久化状态: 使用
AsyncStorage或expo-secure-store
环境变量
// 访问环境变量
const apiUrl = process.env.EXPO_PUBLIC_API_URL
const env = process.env.EXPO_PUBLIC_ENV // 'development' | 'test' | 'production'
// 根据环境执行不同逻辑
if (__DEV__) {
console.log('开发模式')
}
常用命令
# 安装依赖
bun install
# 启动开发服务器
bun run start # 开发环境
bun run start:test # 测试环境
bun run start:prod # 生产环境
# 运行平台
bun run android # Android
bun run ios # iOS
bun run web # Web
# 构建
bun run build:development:android
bun run build:production:ios
# OTA 更新
bun run update:production
# 代码检查
bun run lint
# ICP 备案导出
bun run export:icp:android
bun run export:icp:ios
# Prebuild
bun run prebuild:android
注意事项
- 不要过度设计: 优先简单直接的解决方案
- 代码即文档: 通过清晰的命名和结构表达意图,避免无用注释
- 类型安全: 充分利用 TypeScript,减少运行时错误
- 性能优先: 注意列表渲染、图片加载、状态更新的性能
- 测试覆盖: 为核心功能编写测试(当前项目暂无测试框架)
- 错误监控: 关键操作使用 Sentry 上报错误
- 国际化准备: 使用
i18next管理多语言(已集成但未完全启用)
平台差异处理
// 使用 Platform 处理平台差异
import { Platform } from 'react-native'
const isIOS = Platform.OS === 'ios'
const isAndroid = Platform.OS === 'android'
// 平台特定样式
<Block className={cn(
"p-4",
isIOS && "pt-8",
isAndroid && "pt-4"
)}>
// 平台特定文件
// pointList.native.tsx - 原生平台
// pointList.tsx - Web 平台
AI Agent 协作
项目采用"千门八将"AI Agent 协作模式,详见 AGENTS.md 和 CLAUDE.md:
- 提将(orchestrator): 任务规划与调度
- 正将(code-artisan): 代码编写(追求简洁优雅)
- 反将(architect): 架构设计
- 风将(scout): 代码审查
- 火将(guard): 测试编写
- 除将(fixer): Bug 修复
- 脱将(deploy): 部署发布
- 谣将(researcher): 技术调研
根据任务类型自动调度合适的 Agent 协作完成。
Always use Context7 MCP when I need library/API documentation, code generation, setup or configuration steps without me having to explicitly ask.
记住: 代码是数字时代的文化遗产,每一行都应该有其存在的理由,追求简洁、优雅、高效。