expo 53 项目搭建 react native

1,131 阅读7分钟

🚀 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

📚 下一步

  1. 配置 Storybook - 组件文档化
  2. 集成推送服务 - JPush 或 Expo Notifications
  3. 添加原生模块 - 根据业务需求
  4. 配置 EAS Build - 生产环境构建
  5. 设置 CI/CD - 自动化部署流程

📝 总结

现在您已经拥有一个完整的 Expo 项目架构,包含:

  • 现代化技术栈 - Expo SDK 53 + React Native 0.79
  • 状态管理 - Zustand + React Query
  • UI 体系 - NativeWind + Ant Design
  • 开发工具 - TypeScript + ESLint + Prettier
  • 项目结构 - 清晰的目录组织
  • 配置完整 - 开发和生产环境配置

接下来可以根据具体业务需求添加功能模块和定制化配置!


💡 提示: 建议按步骤逐个配置,每完成一个步骤就测试一下,确保没有问题再继续下一步。