🚀 Expo 现代化医疗应用项目搭建指南
从零开始搭建基于 Expo + React Native 的现代化移动应用,完整的环境配置、依赖安装和项目初始化流程。
📋 目录
🛠️ 环境准备
1. 系统要求
Windows:
- Windows 10 或更高版本
- PowerShell 5.0 或更高版本
macOS:
- macOS 10.15 或更高版本
- Xcode 12 或更高版本(iOS 开发)
Linux:
- Ubuntu 18.04 或更高版本
2. 必要软件安装
# 1. 安装 Node.js (推荐 18.x 或 20.x LTS 版本)
# 访问 https://nodejs.org 下载安装
# 2. 验证 Node.js 安装
node --version # 应该显示 v18.x.x 或 v20.x.x
npm --version # 应该显示 9.x.x 或更高
# 3. 安装 Yarn (推荐使用)
npm install -g yarn
yarn --version
# 4. 安装 Expo CLI
npm install -g @expo/cli
expo --version
3. 移动端开发环境
# Android 开发环境
# 1. 安装 Android Studio
# 2. 配置 Android SDK
# 3. 设置环境变量 ANDROID_HOME
# iOS 开发环境 (仅 macOS)
# 1. 安装 Xcode
# 2. 安装 Xcode Command Line Tools
xcode-select --install
🎯 项目初始化
1. 创建 Expo 项目
# 使用 Expo CLI 创建项目
npx create-expo-app@latest MedicalApp --template
# 选择模板:选择 "Blank (TypeScript)" 或 "Navigation (TypeScript)"
# 推荐选择 Navigation 模板,包含基础导航结构
# 进入项目目录
cd MedicalApp
2. 升级到 Expo Router
# 安装 Expo Router
npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar
# 修改 package.json 中的 main 字段
# "main": "expo-router/entry"
3. 项目基础结构
# 创建基础目录结构
mkdir -p app/{login,\(tabs\),patient,reservation,search}
mkdir -p components/{ui,business,forms,layout,navigation}
mkdir -p stores
mkdir -p utils/{storage,permissions}
mkdir -p assets/{images,fonts,svg}
mkdir -p constants
mkdir -p hooks
mkdir -p types
mkdir -p docs
📦 核心依赖安装
1. 路由和导航
# Expo Router (已安装)
# 底部导航
yarn add @react-navigation/bottom-tabs @react-navigation/native @react-navigation/elements
# 导航相关工具
npx expo install react-native-gesture-handler react-native-reanimated
2. 状态管理
# Zustand - 轻量级状态管理
yarn add zustand
# React Query - 服务端状态管理
yarn add @tanstack/react-query
# 数据持久化
npx expo install @react-native-async-storage/async-storage
3. UI 组件库
# Ant Design React Native
yarn add @ant-design/react-native @ant-design/icons-react-native
# 图标库
yarn add react-native-vector-icons
npx expo install @expo/vector-icons
# 其他 UI 组件
yarn add @react-native-segmented-control/segmented-control
yarn add react-native-calendars
yarn add react-native-swiper
4. 样式和主题
# NativeWind - Tailwind CSS for React Native
yarn add nativewind
yarn add --dev tailwindcss@3.3.2
# 线性渐变
npx expo install expo-linear-gradient
yarn add react-native-linear-gradient
5. 表单处理
# React Hook Form
yarn add react-hook-form @hookform/resolvers
# 表单验证
yarn add yup
# 日期处理
yarn add date-fns dayjs
6. 网络和数据
# HTTP 客户端和工具
yarn add uuid react-native-get-random-values
# WebView
npx expo install react-native-webview
# 图片处理
npx expo install expo-image
# SVG 支持
npx expo install react-native-svg react-native-svg-transformer
7. 设备功能
# 设备信息和功能
npx expo install expo-device expo-application expo-haptics
npx expo install expo-secure-store expo-web-browser expo-blur
npx expo install expo-font expo-splash-screen expo-system-ui
8. 开发工具
# TypeScript 支持
yarn add --dev typescript @types/react @types/react-native-vector-icons
# 代码规范
yarn add --dev eslint eslint-config-expo prettier prettier-plugin-tailwindcss
# 环境变量管理
yarn add --dev cross-env
# API 类型生成
yarn add --dev openapi-ts-request
9. Storybook (可选)
# Storybook for React Native
yarn add --dev @storybook/react-native @storybook/addon-ondevice-actions @storybook/addon-ondevice-backgrounds @storybook/addon-ondevice-controls @storybook/addon-ondevice-notes @storybook/addon-actions
# 其他 Storybook 插件
yarn add --dev @react-native-community/datetimepicker @react-native-community/slider @gorhom/bottom-sheet
⚙️ 基础配置
1. 配置 TypeScript
创建 tsconfig.json:
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./components/*"],
"@/utils/*": ["./utils/*"],
"@/stores/*": ["./stores/*"],
"@/types/*": ["./types/*"],
"@/constants/*": ["./constants/*"],
"@/assets/*": ["./assets/*"]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
]
}
2. 配置 NativeWind
创建 tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,jsx,ts,tsx}",
"./components/**/*.{js,jsx,ts,tsx}"
],
theme: {
extend: {
colors: {
primary: {
50: '#f0fdf4',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
}
}
},
},
plugins: [],
}
创建 global.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
创建 nativewind-env.d.ts:
/// <reference types="nativewind/types" />
3. 配置 Expo 应用
更新 app.config.ts:
import { ExpoConfig } from "expo/config";
export default ({ config }: { config: ExpoConfig }) => {
const environment = process.env.NODE_ENV || 'development';
return {
...config,
name: "医疗管理应用",
slug: "medical-app",
version: "1.0.0",
orientation: "portrait",
icon: "./assets/images/icon.png",
userInterfaceStyle: "automatic",
splash: {
image: "./assets/images/splash-icon.png",
resizeMode: "contain",
backgroundColor: "#ffffff"
},
assetBundlePatterns: [
"**/*"
],
ios: {
supportsTablet: true,
bundleIdentifier: "com.yourcompany.medicalapp"
},
android: {
adaptiveIcon: {
foregroundImage: "./assets/images/adaptive-icon.png",
backgroundColor: "#ffffff"
},
package: "com.yourcompany.medicalapp"
},
web: {
bundler: "metro",
output: "static",
favicon: "./assets/images/favicon.png"
},
plugins: [
"expo-router"
],
experiments: {
typedRoutes: true,
},
extra: {
// 环境变量
EXPO_PUBLIC_API_BASE_URL_DEV: process.env.EXPO_PUBLIC_API_BASE_URL_DEV || 'http://localhost:3000',
EXPO_PUBLIC_API_BASE_URL_PROD: process.env.EXPO_PUBLIC_API_BASE_URL_PROD || 'https://your-api-domain.com',
EXPO_PUBLIC_ENV: environment,
// EAS 配置
eas: {
projectId: "your-project-id-here"
}
},
};
};
4. 配置 Metro
更新 metro.config.js:
const { getDefaultConfig } = require('expo/metro-config');
const { withNativeWind } = require('nativewind/metro');
const config = getDefaultConfig(__dirname);
// NativeWind 配置
config.transformer = {
...config.transformer,
babelTransformerPath: require.resolve('react-native-svg-transformer'),
};
config.resolver = {
...config.resolver,
assetExts: config.resolver.assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...config.resolver.sourceExts, 'svg'],
};
module.exports = withNativeWind(config, { input: './global.css' });
5. 配置 Babel
更新 babel.config.js:
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }]
],
plugins: [
// Reanimated 插件 (必须放在最后)
'react-native-reanimated/plugin',
],
};
};
🏗️ 项目结构搭建
1. 创建基础 Layout
创建 app/_layout.tsx:
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import '../global.css';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 分钟
retry: 2,
},
},
});
export default function RootLayout() {
return (
<QueryClientProvider client={queryClient}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="login" options={{ headerShown: false }} />
</Stack>
<StatusBar style="auto" />
</QueryClientProvider>
);
}
2. 创建 Tab 导航
创建 app/(tabs)/_layout.tsx:
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#22c55e',
tabBarInactiveTintColor: '#6b7280',
headerShown: false,
}}>
<Tabs.Screen
name="index"
options={{
title: '首页',
tabBarIcon: ({ color, focused }) => (
<Ionicons name={focused ? 'home' : 'home-outline'} size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="ai"
options={{
title: 'AI助手',
tabBarIcon: ({ color, focused }) => (
<Ionicons name={focused ? 'chatbubble' : 'chatbubble-outline'} size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="message"
options={{
title: '消息',
tabBarIcon: ({ color, focused }) => (
<Ionicons name={focused ? 'mail' : 'mail-outline'} size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: '我的',
tabBarIcon: ({ color, focused }) => (
<Ionicons name={focused ? 'person' : 'person-outline'} size={24} color={color} />
),
}}
/>
</Tabs>
);
}
3. 创建基础 Store
创建 stores/appStore.ts:
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
interface AppState {
theme: 'light' | 'dark' | 'auto';
language: 'zh-CN' | 'en-US';
isFirstLaunch: boolean;
setTheme: (theme: 'light' | 'dark' | 'auto') => void;
setLanguage: (language: 'zh-CN' | 'en-US') => void;
setFirstLaunch: (isFirst: boolean) => void;
}
export const useAppStore = create<AppState>()(
persist(
(set) => ({
theme: 'auto',
language: 'zh-CN',
isFirstLaunch: true,
setTheme: (theme) => set({ theme }),
setLanguage: (language) => set({ language }),
setFirstLaunch: (isFirst) => set({ isFirstLaunch: isFirst }),
}),
{
name: 'app-storage',
storage: createJSONStorage(() => AsyncStorage),
}
)
);
4. 创建工具函数
创建 utils/request.ts:
interface RequestConfig {
baseURL: string;
timeout: number;
headers: Record<string, string>;
}
class RequestClient {
private config: RequestConfig;
constructor(config: RequestConfig) {
this.config = config;
}
async get<T>(url: string, options?: RequestInit): Promise<T> {
return this.request<T>(url, { ...options, method: 'GET' });
}
async post<T>(url: string, data?: any, options?: RequestInit): Promise<T> {
return this.request<T>(url, {
...options,
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
});
}
private async request<T>(url: string, options: RequestInit): Promise<T> {
const fullUrl = `${this.config.baseURL}${url}`;
const response = await fetch(fullUrl, {
...options,
headers: {
'Content-Type': 'application/json',
...this.config.headers,
...options.headers,
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
}
export const request = new RequestClient({
baseURL: process.env.EXPO_PUBLIC_API_BASE_URL_DEV || 'http://localhost:3000',
timeout: 10000,
headers: {},
});
📱 启动项目
1. 更新 package.json scripts
{
"scripts": {
"start": "expo start --dev-client",
"start:prod": "cross-env NODE_ENV=production expo start --dev-client",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"lint": "expo lint",
"type-check": "tsc --noEmit",
"storybook": "cross-env WITH_STORYBOOK=true expo start --dev-client"
}
}
2. 开发环境启动
# 安装开发客户端
npx expo install expo-dev-client
# 构建开发版本 (首次运行)
npx expo run:android # Android
npx expo run:ios # iOS (仅 macOS)
# 后续开发启动
yarn start
# 或者直接在模拟器中运行
yarn android # Android
yarn ios # iOS
3. 验证安装
创建简单的测试页面 app/(tabs)/index.tsx:
import { View, Text, ScrollView } from 'react-native';
import { useAppStore } from '@/stores/appStore';
export default function HomeScreen() {
const { theme, setTheme } = useAppStore();
return (
<ScrollView className="flex-1 bg-gray-50">
<View className="p-4">
<Text className="text-2xl font-bold text-gray-800 mb-4">
欢迎使用医疗管理应用
</Text>
<View className="bg-white rounded-lg p-4 shadow-sm">
<Text className="text-lg text-gray-600">
当前主题: {theme}
</Text>
<Text className="text-base text-gray-500 mt-2">
✅ Expo Router 已配置
</Text>
<Text className="text-base text-gray-500">
✅ NativeWind 样式正常
</Text>
<Text className="text-base text-gray-500">
✅ Zustand 状态管理工作中
</Text>
<Text className="text-base text-gray-500">
✅ TypeScript 类型检查正常
</Text>
</View>
</View>
</ScrollView>
);
}
🔧 常见问题解决
1. Metro 相关问题
# 清除 Metro 缓存
npx expo start --clear
# 重置 npm/yarn 缓存
npm start -- --reset-cache
# 或
yarn start --reset-cache
2. 依赖版本冲突
# 检查依赖版本兼容性
npx expo doctor
# 修复版本冲突
npx expo install --fix
3. 原生模块问题
# 重新构建原生代码
npx expo run:android --clear
npx expo run:ios --clear
4. TypeScript 错误
# 类型检查
yarn type-check
# 生成类型定义
npx expo customize tsconfig.json
📚 下一步
- 配置 Storybook - 组件文档化
- 集成推送服务 - JPush 或 Expo Notifications
- 添加原生模块 - 根据业务需求
- 配置 EAS Build - 生产环境构建
- 设置 CI/CD - 自动化部署流程
📝 总结
现在您已经拥有一个完整的 Expo 项目架构,包含:
- ✅ 现代化技术栈 - Expo SDK 53 + React Native 0.79
- ✅ 状态管理 - Zustand + React Query
- ✅ UI 体系 - NativeWind + Ant Design
- ✅ 开发工具 - TypeScript + ESLint + Prettier
- ✅ 项目结构 - 清晰的目录组织
- ✅ 配置完整 - 开发和生产环境配置
接下来可以根据具体业务需求添加功能模块和定制化配置!
💡 提示: 建议按步骤逐个配置,每完成一个步骤就测试一下,确保没有问题再继续下一步。