expo 开发 第一章整体架构

287 阅读7分钟

🏥 基于 Expo 打造现代化医疗应用:从零到生产的完整实践

分享一个基于 Expo + React Native 的真实医疗项目架构实践,涵盖现代化技术栈、组件化设计、原生模块集成等核心技术点。

📖 项目背景

最近参与开发了一个医疗行业的移动端管理应用,主要服务于医疗机构的日常管理需求,包括患者管理、预约系统、数据统计等核心功能。项目采用 Expo + React Native 技术栈,在保证开发效率的同时,实现了接近原生的用户体验。

🎯 核心技术栈

框架与工具链

{
  "核心框架": "Expo SDK 53 + React Native 0.79",
  "路由方案": "Expo Router (文件系统路由)",
  "状态管理": "Zustand + React Query",
  "UI 组件库": "Ant Design React Native",
  "样式方案": "NativeWind (Tailwind CSS)",
  "表单处理": "React Hook Form + Yup",
  "开发工具": "TypeScript + Storybook",
  "原生功能": "自定义 Expo 模块"
}

项目结构设计

采用 Expo Router 的文件系统路由,目录结构清晰且符合现代前端开发习惯:

├── app/                    # 路由页面
│   ├── (tabs)/            # Tab 导航页面
│   │   ├── index.tsx      # 首页
│   │   ├── ai.tsx         # AI 助手
│   │   ├── message.tsx    # 消息中心
│   │   └── profile.tsx    # 个人中心
│   ├── patient/           # 患者管理模块
│   ├── reservation/       # 预约管理模块
│   └── login.tsx          # 登录页面
├── components/            # 通用组件库
├── stores/               # 状态管理
├── utils/                # 工具函数
├── modules/              # 自定义原生模块
└── docs/                 # 完整文档体系

🏗️ 架构设计亮点

1. 现代化状态管理方案

结合 ZustandReact Query 实现了完整的数据流管理:

// stores/dataStatsStore.ts - 业务状态管理
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface DataStatsState {
  selectedTimeRange: string;
  departmentId: string;
  setTimeRange: (range: string) => void;
  setDepartment: (id: string) => void;
}

export const useDataStatsStore = create<DataStatsState>()(
  persist(
    (set) => ({
      selectedTimeRange: 'week',
      departmentId: '',
      setTimeRange: (range) => set({ selectedTimeRange: range }),
      setDepartment: (id) => set({ departmentId: id }),
    }),
    { name: 'data-stats-storage' }
  )
);

// hooks/useDataStatistics.ts - 数据获取层
import { useQuery } from '@tanstack/react-query';

export const useDataStatistics = () => {
  const { departmentId, selectedTimeRange } = useDataStatsStore();
  
  return useQuery({
    queryKey: ['dataStats', departmentId, selectedTimeRange],
    queryFn: () => fetchDataStatistics({ departmentId, selectedTimeRange }),
    staleTime: 5 * 60 * 1000, // 5分钟缓存
    refetchOnWindowFocus: true,
  });
};

架构优势

  • 状态分离:UI 状态用 Zustand,服务端状态用 React Query
  • 自动缓存:智能缓存机制,减少不必要的网络请求
  • 数据同步:自动重试、后台刷新、实时同步
  • 类型安全:TypeScript 全覆盖,编译时错误检查

2. 组件化设计体系

构建了完整的组件库,每个组件都有对应的 Storybook 文档:

// components/business/DataStatsGrid.tsx
interface DataStatsGridProps {
  data?: StatsData;
  isLoading?: boolean;
  onRefresh?: () => void;
}

export const DataStatsGrid: React.FC<DataStatsGridProps> = ({
  data,
  isLoading,
  onRefresh
}) => {
  return (
    <View className="bg-white rounded-lg p-4 mx-4 shadow-sm">
      <View className="flex-row justify-between items-center mb-4">
        <Text className="text-lg font-semibold text-gray-800">数据统计</Text>
        <TouchableOpacity onPress={onRefresh} disabled={isLoading}>
          <Ionicons 
            name="refresh" 
            size={20} 
            color={isLoading ? '#ccc' : '#666'} 
          />
        </TouchableOpacity>
      </View>
      
      <View className="flex-row flex-wrap">
        {statsItems.map((item, index) => (
          <StatsItem
            key={index}
            title={item.title}
            value={data?.[item.key] || 0}
            icon={item.icon}
            trend={item.trend}
          />
        ))}
      </View>
    </View>
  );
};

设计特色

  • 🎨 原子化组件:可复用、可组合的组件设计
  • 📚 文档驱动:71% 组件覆盖率,59+ Storybook Stories
  • 🎯 TypeScript:完整的类型定义和 Props 约束
  • 🔄 响应式:基于 NativeWind 的响应式布局

3. 自定义原生模块集成

针对特殊业务需求,开发了自定义的 Expo 模块:

// modules/my-module/src/WxModule.ts
import { NativeModule, requireNativeModule } from 'expo';

export interface WxModuleEvents {
  onWeworkAuthResult: (result: { code?: string; error?: string }) => void;
}

class WxModule extends NativeModule<WxModuleEvents> {
  // 第三方平台登录
  async thirdPartyAuth(corpId: string, agentId: string): Promise<boolean> {
    return await this.nativeModule.thirdPartyAuth(corpId, agentId);
  }

  // 注册深度链接处理
  registerWeworkCallback(callback: (result: any) => void): void {
    this.addListener('onWeworkAuthResult', callback);
  }
}

export default requireNativeModule('WxModule');

原生集成特色

  • 📱 第三方登录:完整的第三方 SDK 集成,支持免密登录
  • 🔔 极光推送:智能推送服务,支持多端消息同步
  • 🔗 深度链接:应用内跳转和外部唤起支持
  • 性能优化:原生模块处理计算密集型任务

💡 核心功能实现

1. 智能首页设计

// app/(tabs)/index.tsx
export default function HomePage() {
  const { data: statsData, isLoading, refetch } = useDataStatistics();
  const { selectedDepartment } = useDepartmentStore();

  return (
    <ParallaxScrollView
      headerBackgroundColor={{ light: '#D0EFFF', dark: '#353636' }}
      headerImage={<Image source={require('@/assets/images/hero-bg.png')} />}
    >
      {/* 部门选择器 */}
      <DepartmentSelector />
      
      {/* 搜索栏 */}
      <SearchBar placeholder="搜索内容..." />
      
      {/* 主功能网格 */}
      <MainFunctionGrid />
      
      {/* 数据统计 */}
      <DataStatsGrid
        data={statsData}
        isLoading={isLoading}
        onRefresh={refetch}
      />
      
      {/* AI 助手功能卡片 */}
      <AIAssistantCard />
    </ParallaxScrollView>
  );
}

设计亮点

  • 🎨 视觉层次:渐变背景 + 卡片式布局
  • 📊 数据驱动:实时数据统计和趋势展示
  • 🤖 AI 集成:智能助手功能入口
  • 🔍 快速操作:搜索、筛选、跳转一体化

2. 表单处理最佳实践

// 患者信息表单
import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';

const patientSchema = yup.object({
  name: yup.string().required('请输入患者姓名'),
  idCard: yup.string().matches(ID_CARD_REGEX, '身份证格式不正确'),
  phone: yup.string().matches(PHONE_REGEX, '手机号格式不正确'),
});

export const PatientForm = () => {
  const { control, handleSubmit, formState: { errors } } = useForm({
    resolver: yupResolver(patientSchema),
    defaultValues: {
      name: '',
      idCard: '',
      phone: '',
    },
  });

  const { mutate: createPatient, isLoading } = useMutation({
    mutationFn: createPatientApi,
    onSuccess: () => {
      Toast.show({ type: 'success', text1: '患者信息已保存' });
      router.back();
    },
  });

  return (
    <View className="p-4">
      <Controller
        control={control}
        name="name"
        render={({ field: { onChange, value } }) => (
          <FormField
            label="患者姓名"
            value={value}
            onChangeText={onChange}
            error={errors.name?.message}
          />
        )}
      />
      {/* 其他表单项... */}
    </View>
  );
};

🚀 性能优化实践

1. 图片和资源优化

// 使用 Expo Image 优化图片加载
import { Image } from 'expo-image';

export const OptimizedImage = ({ source, ...props }) => (
  <Image
    source={source}
    placeholder={{ blurhash: 'L6PZfSi_.AyE_3t7t7R**0o#DgR4' }}
    contentFit="cover"
    transition={200}
    {...props}
  />
);

2. 列表性能优化

// 大数据列表优化
import { FlashList } from '@shopify/flash-list';

export const PatientList = ({ patients }) => (
  <FlashList
    data={patients}
    renderItem={({ item }) => <PatientCard patient={item} />}
    estimatedItemSize={80}
    keyExtractor={(item) => item.id}
    onEndReachedThreshold={0.1}
    onEndReached={loadMorePatients}
  />
);

📱 多端适配策略

1. 响应式布局

// 使用 NativeWind 实现响应式设计
export const StatsGrid = () => (
  <View className="flex-row flex-wrap">
    {statsData.map((stat) => (
      <View 
        key={stat.id} 
        className="w-1/2 md:w-1/4 p-2" // 移动端 2 平板 4 
      >
        <StatsCard {...stat} />
      </View>
    ))}
  </View>
);

2. 平台适配

// 平台特定的状态栏处理
import { Platform } from 'react-native';
import { StatusBar } from 'expo-status-bar';

export const DynamicStatusBar = ({ route }) => {
  const getStatusBarStyle = () => {
    if (Platform.OS === 'ios') {
      return route.name === 'login' ? 'light' : 'dark';
    }
    return 'auto';
  };

  return <StatusBar style={getStatusBarStyle()} />;
};

🔧 开发工具链

1. Storybook 组件文档化

// components/DataStatsGrid.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { DataStatsGrid } from './DataStatsGrid';

const meta: Meta<typeof DataStatsGrid> = {
  title: 'Business/DataStatsGrid',
  component: DataStatsGrid,
  parameters: {
    docs: {
      description: {
        component: '数据统计网格组件,展示各类业务数据统计',
      },
    },
  },
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
  args: {
    data: mockStatsData,
    isLoading: false,
  },
};

export const Loading: Story = {
  args: {
    isLoading: true,
  },
};

2. 类型安全的 API 层

// 使用 openapi-ts-request 自动生成类型
import { request } from '@/utils/request';
import type { DataStatisticsResponse } from '@/types/api';

export const dataStatsApi = {
  // 获取数据统计
  getDataStatistics: (params: StatsParams): Promise<DataStatisticsResponse> =>
    request.get('/api/data-statistics', { params }),
    
  // 获取部门列表
  getDepartments: (): Promise<Department[]> =>
    request.get('/api/departments'),
};

🔐 安全性考虑

1. Token 管理

// utils/auth.ts
import * as SecureStore from 'expo-secure-store';

export const tokenManager = {
  async setToken(token: string): Promise<void> {
    await SecureStore.setItemAsync('auth_token', token);
  },

  async getToken(): Promise<string | null> {
    return await SecureStore.getItemAsync('auth_token');
  },

  async removeToken(): Promise<void> {
    await SecureStore.deleteItemAsync('auth_token');
  },
};

2. 权限管理

// utils/permissions.ts
import * as Notifications from 'expo-notifications';

export const requestNotificationPermission = async () => {
  const { status: existingStatus } = await Notifications.getPermissionsAsync();
  
  if (existingStatus !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    return status === 'granted';
  }
  
  return true;
};

🌟 项目成果

技术指标

  • 启动性能:冷启动时间 < 3s
  • 📦 包体积:Android APK < 50MB
  • 🔄 组件复用率:71% 组件已文档化
  • 🧪 代码覆盖率:核心业务逻辑 > 80%

业务价值

  • 👥 用户体验:统一的设计语言和交互模式
  • 🔧 开发效率:组件化开发,新功能开发周期缩短 40%
  • 📱 多端一致性:iOS/Android 体验一致性 > 95%
  • 🛠️ 维护成本:模块化架构,维护成本降低 60%

🎯 最佳实践总结

1. 架构设计

  • 状态分离:UI 状态与服务端状态分离管理
  • 组件化:原子化组件设计,提高复用性
  • 类型安全:TypeScript 全面覆盖,减少运行时错误
  • 文档驱动:Storybook 保证组件质量和可维护性

2. 性能优化

  • 懒加载:路由级别的代码分割
  • 缓存策略:智能的数据缓存和更新机制
  • 图片优化:WebP 格式 + CDN + 懒加载
  • 包体优化:Tree-shaking + 动态导入

3. 开发体验

  • 热重载:Expo Dev Client 提供原生级热重载
  • 调试工具:Flipper + React Query DevTools
  • 自动化:ESLint + Prettier + 自动化 API 类型生成
  • 团队协作:统一的代码规范和组件文档

🔮 未来规划

  1. 微前端架构:模块化拆分,支持独立部署
  2. 离线支持:Service Worker + 本地存储同步
  3. AI 增强:更深度的 AI 功能集成
  4. 性能监控:Sentry + 自定义性能指标
  5. 自动化测试:E2E 测试覆盖率提升

💭 结语

通过这个项目的实践,深刻体会到 Expo + React Native 在企业级应用开发中的强大能力。合理的架构设计、现代化的技术栈选择,以及完善的工程化配置,是项目成功的关键因素。

希望这篇分享能对正在使用或考虑使用 Expo 开发的朋友们有所帮助。如果你有任何问题或想要了解更多技术细节,欢迎在评论区交流讨论!


技术栈标签Expo React Native TypeScript Zustand React Query 医疗应用 移动开发

👍 如果这篇文章对你有帮助,别忘了点赞和收藏哦!也欢迎关注我,后续会分享更多前端技术实践经验。