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

基于 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 医疗应用 移动开发

如果这篇文章对你有帮助,别忘了点赞和收藏哦!也欢迎关注我,后续会分享更多前端技术实践经验。
————————————————
版权声明:本文为CSDN博主「疯狂求学的小白」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_60678785/article/details/149019316

你可能感兴趣的:(基于 Expo 打造现代化医疗应用:从零到生产的完整实践)