mobile-app
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMobile App Development Expertise
移动应用开发专业指南
Overview
概述
A guide for developing mobile apps based on web development experience.
Develop for iOS and Android simultaneously using cross-platform frameworks.
一份基于Web开发经验的移动应用开发指南。
使用跨平台框架同时为iOS和Android进行开发。
Framework Selection Guide
框架选择指南
Framework Selection by Tier (v1.3.0)
按层级划分的框架选择(v1.3.0版)
| Framework | Tier | Recommendation | Use Case |
|---|---|---|---|
| React Native (Expo) | Tier 1 | ⭐ Primary | TypeScript ecosystem, AI tools |
| React Native CLI | Tier 1 | Recommended | Native module needs |
| Flutter | Tier 2 | Supported | Multi-platform (6 OS), performance |
AI-Native Recommendation: React Native with TypeScript
- Full Copilot/Claude support
- Extensive npm ecosystem
- 20:1 developer availability vs Dart
Performance Recommendation: Flutter
- Impeller rendering engine
- Single codebase for 6 platforms
- Smaller bundles
| Framework | Tier | 推荐等级 | 适用场景 |
|---|---|---|---|
| React Native (Expo) | Tier 1 | ⭐ 首选 | TypeScript生态系统、AI工具 |
| React Native CLI | Tier 1 | 推荐 | 需要原生模块 |
| Flutter | Tier 2 | 支持 | 多平台(6种操作系统)、性能优先 |
原生AI支持推荐:React Native + TypeScript
- 完整支持Copilot/Claude
- 丰富的npm生态系统
- 开发者可用度是Dart的20:1
性能推荐:Flutter
- Impeller渲染引擎
- 单一代码库适配6种平台
- 更小的安装包
Level-wise Recommendations
按开发阶段的推荐
Starter → Expo (React Native) [Tier 1]
- Simple setup, can leverage web knowledge
- Full AI tool support
Dynamic → Expo + EAS Build [Tier 1] or Flutter [Tier 2]
- Includes server integration, production build support
- Choose Flutter for multi-platform needs
Enterprise → React Native CLI [Tier 1] or Flutter [Tier 2]
- Complex native features, performance optimization needed
- Flutter for consistent cross-platform UI入门阶段 → Expo (React Native) [Tier 1]
- 配置简单,可复用Web开发知识
- 完整支持AI工具
进阶阶段 → Expo + EAS Build [Tier 1] 或 Flutter [Tier 2]
- 包含服务端集成、生产构建支持
- 如需多平台适配选择Flutter
企业级阶段 → React Native CLI [Tier 1] 或 Flutter [Tier 2]
- 需要复杂原生功能、性能优化
- 追求跨平台UI一致性选FlutterExpo (React Native) Guide
Expo (React Native) 指南
Project Creation
项目创建
bash
undefinedbash
undefinedInstall Expo CLI
Install Expo CLI
npm install -g expo-cli
npm install -g expo-cli
Create new project
Create new project
npx create-expo-app my-app
cd my-app
npx create-expo-app my-app
cd my-app
Start development server
Start development server
npx expo start
undefinednpx expo start
undefinedFolder Structure
文件夹结构
my-app/
├── app/ # Expo Router pages
│ ├── (tabs)/ # Tab navigation
│ │ ├── index.tsx # Home tab
│ │ ├── explore.tsx # Explore tab
│ │ └── _layout.tsx # Tab layout
│ ├── _layout.tsx # Root layout
│ └── +not-found.tsx # 404 page
├── components/ # Reusable components
├── hooks/ # Custom hooks
├── constants/ # Constants
├── assets/ # Images, fonts, etc.
├── app.json # Expo configuration
└── package.jsonmy-app/
├── app/ # Expo Router 页面
│ ├── (tabs)/ # 标签页导航
│ │ ├── index.tsx # 首页标签
│ │ ├── explore.tsx # 探索页标签
│ │ └── _layout.tsx # 标签页布局
│ ├── _layout.tsx # 根布局
│ └── +not-found.tsx # 404页面
├── components/ # 可复用组件
├── hooks/ # 自定义hooks
├── constants/ # 常量
├── assets/ # 图片、字体等资源
├── app.json # Expo配置文件
└── package.jsonNavigation Patterns
导航模式
typescript
// app/_layout.tsx - Stack navigation
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
</Stack>
);
}typescript
// app/(tabs)/_layout.tsx - Tab navigation
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
export default function TabLayout() {
return (
<Tabs>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color }) => <Ionicons name="home" color={color} size={24} />,
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profile',
tabBarIcon: ({ color }) => <Ionicons name="person" color={color} size={24} />,
}}
/>
</Tabs>
);
}typescript
// app/_layout.tsx - 栈式导航
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
</Stack>
);
}typescript
// app/(tabs)/_layout.tsx - 标签页导航
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
export default function TabLayout() {
return (
<Tabs>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color }) => <Ionicons name="home" color={color} size={24} />,
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profile',
tabBarIcon: ({ color }) => <Ionicons name="person" color={color} size={24} />,
}}
/>
</Tabs>
);
}Styling Patterns
样式模式
typescript
// Basic StyleSheet
import { StyleSheet, View, Text } from 'react-native';
export function MyComponent() {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#fff',
},
title: {
fontSize: 24,
fontWeight: 'bold',
},
});typescript
// NativeWind (Tailwind for RN) - Recommended
import { View, Text } from 'react-native';
export function MyComponent() {
return (
<View className="flex-1 p-4 bg-white">
<Text className="text-2xl font-bold">Hello</Text>
</View>
);
}typescript
// Basic StyleSheet
import { StyleSheet, View, Text } from 'react-native';
export function MyComponent() {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#fff',
},
title: {
fontSize: 24,
fontWeight: 'bold',
},
});typescript
// NativeWind (Tailwind for RN) - Recommended
import { View, Text } from 'react-native';
export function MyComponent() {
return (
<View className="flex-1 p-4 bg-white">
<Text className="text-2xl font-bold">Hello</Text>
</View>
);
}API Integration
API集成
typescript
// hooks/useApi.ts
import { useState, useEffect } from 'react';
export function useApi<T>(endpoint: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}${endpoint}`);
if (!response.ok) throw new Error('API Error');
const json = await response.json();
setData(json);
} catch (e) {
setError(e as Error);
} finally {
setLoading(false);
}
};
fetchData();
}, [endpoint]);
return { data, loading, error };
}typescript
// hooks/useApi.ts
import { useState, useEffect } from 'react';
export function useApi<T>(endpoint: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}${endpoint}`);
if (!response.ok) throw new Error('API Error');
const json = await response.json();
setData(json);
} catch (e) {
setError(e as Error);
} finally {
setLoading(false);
}
};
fetchData();
}, [endpoint]);
return { data, loading, error };
}Authentication Pattern
认证模式
typescript
// context/AuthContext.tsx
import { createContext, useContext, useState, useEffect } from 'react';
import * as SecureStore from 'expo-secure-store';
interface AuthContextType {
user: User | null;
signIn: (email: string, password: string) => Promise<void>;
signOut: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
// Check for stored token on app start
const loadToken = async () => {
const token = await SecureStore.getItemAsync('authToken');
if (token) {
// Load user info with token
}
};
loadToken();
}, []);
const signIn = async (email: string, password: string) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
const { token, user } = await response.json();
await SecureStore.setItemAsync('authToken', token);
setUser(user);
};
const signOut = async () => {
await SecureStore.deleteItemAsync('authToken');
setUser(null);
};
return (
<AuthContext.Provider value={{ user, signIn, signOut }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext)!;typescript
// context/AuthContext.tsx
import { createContext, useContext, useState, useEffect } from 'react';
import * as SecureStore from 'expo-secure-store';
interface AuthContextType {
user: User | null;
signIn: (email: string, password: string) => Promise<void>;
signOut: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
// Check for stored token on app start
const loadToken = async () => {
const token = await SecureStore.getItemAsync('authToken');
if (token) {
// Load user info with token
}
};
loadToken();
}, []);
const signIn = async (email: string, password: string) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
const { token, user } = await response.json();
await SecureStore.setItemAsync('authToken', token);
setUser(user);
};
const signOut = async () => {
await SecureStore.deleteItemAsync('authToken');
setUser(null);
};
return (
<AuthContext.Provider value={{ user, signIn, signOut }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext)!;Flutter Guide
Flutter 指南
Project Creation
项目创建
bash
undefinedbash
undefinedAfter installing Flutter SDK
After installing Flutter SDK
flutter create my_app
cd my_app
flutter create my_app
cd my_app
Start development server
Start development server
flutter run
undefinedflutter run
undefinedFolder Structure
文件夹结构
my_app/
├── lib/
│ ├── main.dart # App entry point
│ ├── app/
│ │ ├── app.dart # MaterialApp setup
│ │ └── routes.dart # Route definitions
│ ├── features/ # Feature-based folders
│ │ ├── auth/
│ │ │ ├── screens/
│ │ │ ├── widgets/
│ │ │ └── providers/
│ │ └── home/
│ ├── shared/
│ │ ├── widgets/ # Common widgets
│ │ ├── services/ # API services
│ │ └── models/ # Data models
│ └── core/
│ ├── theme/ # Theme settings
│ └── constants/ # Constants
├── assets/ # Images, fonts
├── pubspec.yaml # Dependency management
└── android/ & ios/ # Native codemy_app/
├── lib/
│ ├── main.dart # 应用入口
│ ├── app/
│ │ ├── app.dart # MaterialApp配置
│ │ └── routes.dart # 路由定义
│ ├── features/ # 基于功能的文件夹
│ │ ├── auth/
│ │ │ ├── screens/
│ │ │ ├── widgets/
│ │ │ └── providers/
│ │ └── home/
│ ├── shared/
│ │ ├── widgets/ # 通用组件
│ │ ├── services/ # API服务
│ │ └── models/ # 数据模型
│ └── core/
│ ├── theme/ # 主题设置
│ └── constants/ # 常量
├── assets/ # 图片、字体
├── pubspec.yaml # 依赖管理
└── android/ & ios/ # 原生代码Basic Widget Patterns
基础组件模式
dart
// lib/features/home/screens/home_screen.dart
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: const Center(
child: Text('Hello, Flutter!'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
}
}dart
// lib/features/home/screens/home_screen.dart
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: const Center(
child: Text('Hello, Flutter!'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
}
}State Management (Riverpod)
状态管理(Riverpod)
dart
// lib/providers/counter_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}dart
// Usage
class CounterScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
}dart
// lib/providers/counter_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}dart
// Usage
class CounterScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
}Web vs Mobile Differences
Web与移动应用的差异
UI/UX Differences
UI/UX差异
| Element | Web | Mobile |
|---|---|---|
| Click | onClick | onPress |
| Scroll | overflow: scroll | ScrollView / FlatList |
| Input | input | TextInput |
| Links | a href | Link / navigation |
| Layout | div + CSS | View + StyleSheet |
| 元素 | Web | 移动应用 |
|---|---|---|
| 点击事件 | onClick | onPress |
| 滚动 | overflow: scroll | ScrollView / FlatList |
| 输入框 | input | TextInput |
| 链接 | a href | Link / 导航API |
| 布局 | div + CSS | View + StyleSheet |
Navigation Differences
导航差异
Web: URL-based (browser back button)
Mobile: Stack-based (screen stacking)
Web: /users/123
Mobile: navigation.navigate('User', { id: 123 })Web: 基于URL(浏览器返回键)
移动应用: 基于栈(页面堆叠)
Web: /users/123
移动应用: navigation.navigate('User', { id: 123 })Storage Differences
存储差异
Web: localStorage, sessionStorage, Cookie
Mobile: AsyncStorage, SecureStore, SQLite
⚠️ SecureStore is required for sensitive info on mobile!Web: localStorage, sessionStorage, Cookie
移动应用: AsyncStorage, SecureStore, SQLite
⚠️ 移动应用中敏感信息必须使用SecureStore!Build & Deployment
构建与部署
Expo EAS Build
Expo EAS Build
bash
undefinedbash
undefinedInstall EAS CLI
Install EAS CLI
npm install -g eas-cli
npm install -g eas-cli
Login
Login
eas login
eas login
Configure build
Configure build
eas build:configure
eas build:configure
iOS build
iOS build
eas build --platform ios
eas build --platform ios
Android build
Android build
eas build --platform android
eas build --platform android
Submit to stores
Submit to stores
eas submit --platform ios
eas submit --platform android
undefinedeas submit --platform ios
eas submit --platform android
undefinedEnvironment Variables
环境变量
json
// app.json
{
"expo": {
"extra": {
"apiUrl": "https://api.example.com"
}
}
}typescript
// Usage
import Constants from 'expo-constants';
const apiUrl = Constants.expoConfig?.extra?.apiUrl;json
// app.json
{
"expo": {
"extra": {
"apiUrl": "https://api.example.com"
}
}
}typescript
// Usage
import Constants from 'expo-constants';
const apiUrl = Constants.expoConfig?.extra?.apiUrl;Mobile PDCA Checklist
移动应用PDCA检查清单
Phase 1: Schema
阶段1:架构设计
□ Identify data that needs offline caching
□ Define sync conflict resolution strategy□ 确定需要离线缓存的数据
□ 定义同步冲突解决策略Phase 3: Mockup
阶段3:原型设计
□ Follow iOS/Android native UX guidelines
□ Consider gestures (swipe, pinch, etc.)
□ Layout for different screen sizes (phone, tablet)□ 遵循iOS/Android原生UX指南
□ 考虑手势操作(滑动、捏合等)
□ 适配不同屏幕尺寸(手机、平板)Phase 6: UI
阶段6:UI开发
□ Keyboard handling (screen adjustment during input)
□ Safe Area handling (notch, home button area)
□ Handle platform-specific UI differences□ 键盘处理(输入时调整屏幕)
□ 安全区域处理(刘海屏、Home键区域)
□ 处理平台特定UI差异Phase 7: Security
阶段7:安全
□ Store sensitive info with SecureStore
□ Certificate Pinning (if needed)
□ App obfuscation settings□ 使用SecureStore存储敏感信息
□ 证书绑定(如需)
□ 应用混淆设置Phase 9: Deployment
阶段9:部署
□ Follow App Store review guidelines
□ Prepare Privacy Policy URL
□ Prepare screenshots, app description□ 遵循应用商店审核指南
□ 准备隐私政策URL
□ 准备截图、应用描述Frequently Asked Questions
常见问题
Q: Can I convert a web project to an app?
Q: 能否将Web项目转为移动应用?
A: Recommend separate project over full conversion
- APIs can be shared
- UI needs to be rewritten (for native UX)
- Business logic can be sharedA: 建议新建项目而非直接转换
- API可共享
- UI需要重写(以适配原生UX)
- 业务逻辑可共享Q: Should I use Expo or React Native CLI?
Q: 应该使用Expo还是React Native CLI?
A: Start with Expo!
- 90%+ of apps are sufficient with Expo
- Can eject later if needed
- Use CLI only when native modules are absolutely requiredA: 从Expo开始!
- 90%以上的应用使用Expo即可满足需求
- 后续可按需导出为CLI项目
- 仅当必须使用原生模块时才选择CLIQ: How long does app review take?
Q: 应用审核需要多长时间?
A:
- iOS: 1-7 days (average 2-3 days)
- Android: Few hours ~ 3 days
⚠️ First submission has high rejection possibility → Follow guidelines carefully!A:
- iOS: 1-7天(平均2-3天)
- Android: 数小时 ~ 3天
⚠️ 首次提交被拒绝的概率较高 → 请严格遵循指南!Requesting from Claude
向Claude提出需求的示例
Project Creation
项目创建
"Set up a [app description] app project with React Native + Expo.
Configure with 3 tab navigation (Home, Search, Profile).""使用React Native + Expo搭建一个[应用描述]的项目。
配置3个标签页导航(首页、搜索、个人中心)。"Screen Implementation
页面实现
"Implement [screen name] screen.
- Display [content] at the top
- Display [list/form/etc.] in the middle
- [Button/navigation] at the bottom""实现[页面名称]页面。
- 顶部展示[内容]
- 中部展示[列表/表单等]
- 底部添加[按钮/导航]"API Integration
API集成
"Implement screen integrating with [API endpoint].
- Show loading state
- Handle errors
- Support pull-to-refresh""实现集成[API端点]的页面。
- 展示加载状态
- 处理错误
- 支持下拉刷新"