项目结构 #
一、整体结构 #
1.1 标准项目结构 #
text
my-app/
├── src/ # Web源码目录
│ ├── components/ # 组件目录
│ ├── pages/ # 页面目录
│ ├── hooks/ # 自定义Hooks
│ ├── utils/ # 工具函数
│ ├── assets/ # 静态资源
│ ├── App.tsx # 根组件
│ ├── main.tsx # 入口文件
│ └── index.css # 全局样式
│
├── public/ # 公共静态资源
│ └── favicon.ico
│
├── dist/ # Web构建输出(webDir)
│ ├── index.html
│ ├── assets/
│ └── ...
│
├── ios/ # iOS原生项目
│ └── App/
│ ├── App.xcworkspace # Xcode工作空间
│ ├── App/
│ │ ├── AppDelegate.swift
│ │ ├── Info.plist
│ │ ├── Assets.xcassets
│ │ └── capacitor.config.json
│ └── Podfile # CocoaPods配置
│
├── android/ # Android原生项目
│ ├── app/
│ │ ├── src/
│ │ │ └── main/
│ │ │ ├── java/com/example/app/
│ │ │ │ └── MainActivity.java
│ │ │ ├── res/
│ │ │ │ ├── values/
│ │ │ │ │ └── strings.xml
│ │ │ │ └── mipmap-*/
│ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle/
│ ├── build.gradle
│ ├── settings.gradle
│ └── capacitor.config.json
│
├── capacitor.config.json # Capacitor主配置
├── capacitor.config.ts # TypeScript配置(可选)
├── package.json # npm配置
├── tsconfig.json # TypeScript配置
├── vite.config.ts # Vite配置
└── README.md
二、Web源码目录 #
2.1 src目录 #
text
src/
├── components/ # 可复用组件
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.css
│ │ └── index.ts
│ └── Modal/
│ ├── Modal.tsx
│ └── Modal.css
│
├── pages/ # 页面组件
│ ├── Home/
│ │ └── Home.tsx
│ └── Settings/
│ └── Settings.tsx
│
├── hooks/ # 自定义Hooks
│ ├── useStorage.ts
│ └── useNetwork.ts
│
├── services/ # 服务层
│ ├── api.ts
│ └── storage.ts
│
├── utils/ # 工具函数
│ ├── format.ts
│ └── validate.ts
│
├── types/ # TypeScript类型
│ └── index.d.ts
│
├── constants/ # 常量定义
│ └── index.ts
│
├── App.tsx # 根组件
├── main.tsx # 入口文件
└── index.css # 全局样式
2.2 入口文件配置 #
tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { setupCapacitor } from './capacitor-setup';
import './index.css';
const init = async () => {
await setupCapacitor();
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
};
init();
2.3 构建输出目录 #
text
dist/
├── index.html # 入口HTML
├── assets/
│ ├── index-[hash].js # 打包后的JS
│ ├── index-[hash].css # 打包后的CSS
│ └── logo-[hash].png # 图片资源
└── favicon.ico
三、Capacitor配置 #
3.1 capacitor.config.json #
json
{
"appId": "com.example.myapp",
"appName": "My App",
"webDir": "dist",
"bundledWebRuntime": false,
"server": {
"iosScheme": "https",
"androidScheme": "https",
"url": "http://localhost:5173",
"cleartext": true
},
"ios": {
"contentInset": "automatic",
"preferredContentMode": "mobile",
"scrollEnabled": true,
"backgroundColor": "#ffffff"
},
"android": {
"allowMixedContent": true,
"captureInput": true,
"webContentsDebuggingEnabled": true,
"backgroundColor": "#ffffff"
},
"plugins": {
"SplashScreen": {
"launchShowDuration": 2000,
"backgroundColor": "#ffffff",
"showSpinner": false
},
"StatusBar": {
"style": "DARK",
"backgroundColor": "#4a90d9"
},
"Keyboard": {
"resize": "body",
"resizeOnFullScreen": true
}
}
}
3.2 capacitor.config.ts(TypeScript版本) #
typescript
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'My App',
webDir: 'dist',
bundledWebRuntime: false,
server: {
iosScheme: 'https',
androidScheme: 'https'
},
ios: {
contentInset: 'automatic'
},
android: {
allowMixedContent: true
},
plugins: {
SplashScreen: {
launchShowDuration: 2000,
backgroundColor: '#ffffff'
}
}
};
export default config;
3.3 配置项详解 #
| 配置项 | 类型 | 说明 |
|---|---|---|
| appId | string | 应用包名(反向域名格式) |
| appName | string | 应用显示名称 |
| webDir | string | Web构建输出目录 |
| bundledWebRuntime | boolean | 是否打包Capacitor运行时 |
| server | object | 服务器配置 |
| ios | object | iOS特定配置 |
| android | object | Android特定配置 |
| plugins | object | 插件配置 |
3.4 服务器配置 #
typescript
interface ServerConfig {
url?: string; // 开发服务器URL
iosScheme?: 'http' | 'https'; // iOS URL scheme
androidScheme?: 'http' | 'https'; // Android URL scheme
cleartext?: boolean; // 允许HTTP明文传输
hostname?: string; // 主机名
port?: number; // 端口号
}
四、iOS项目结构 #
4.1 目录结构 #
text
ios/
└── App/
├── App.xcworkspace # Xcode工作空间(使用这个打开)
├── App.xcodeproj # Xcode项目
│ ├── project.pbxproj
│ └── xcshareddata/
│
├── App/ # 主应用目录
│ ├── AppDelegate.swift # 应用代理
│ ├── SceneDelegate.swift # 场景代理(iOS 13+)
│ ├── ViewController.swift
│ ├── Info.plist # 应用配置
│ ├── Assets.xcassets/ # 资源目录
│ │ ├── AppIcon.appiconset/
│ │ ├── LaunchImage.launchimage/
│ │ └── Splash.imageset/
│ ├── Base.lproj/
│ │ └── LaunchScreen.storyboard
│ └── capacitor.config.json
│
├── App.xcscheme # 构建方案
├── Podfile # CocoaPods依赖
├── Podfile.lock
└── Pods/ # CocoaPods安装的依赖
4.2 AppDelegate.swift #
swift
import UIKit
import Capacitor
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
}
4.3 Info.plist配置 #
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>My App</string>
<key>CFBundleIdentifier</key>
<string>com.example.myapp</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiresFullScreen</key>
<false/>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<!-- 权限描述 -->
<key>NSCameraUsageDescription</key>
<string>需要访问相机来拍照</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册来选择照片</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要获取您的位置信息</string>
</dict>
</plist>
4.4 Podfile #
ruby
require_relative '../node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '13.0'
use_frameworks!
target 'App' do
capacitor_pods
# 添加其他依赖
# pod 'Alamofire'
end
五、Android项目结构 #
5.1 目录结构 #
text
android/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/myapp/
│ │ │ │ ├── MainActivity.java # 主Activity
│ │ │ │ └── MyApplication.java # Application类
│ │ │ │
│ │ │ ├── res/
│ │ │ │ ├── values/
│ │ │ │ │ ├── strings.xml # 字符串资源
│ │ │ │ │ ├── styles.xml # 样式
│ │ │ │ │ └── colors.xml # 颜色
│ │ │ │ │
│ │ │ │ ├── mipmap-hdpi/
│ │ │ │ │ └── ic_launcher.png # 应用图标
│ │ │ │ │
│ │ │ │ ├── mipmap-mdpi/
│ │ │ │ ├── mipmap-xhdpi/
│ │ │ │ ├── mipmap-xxhdpi/
│ │ │ │ └── mipmap-xxxhdpi/
│ │ │ │
│ │ │ ├── assets/
│ │ │ │ └── capacitor.config.json
│ │ │ │
│ │ │ └── AndroidManifest.xml # 应用清单
│ │ │
│ │ ├── debug/ # Debug配置
│ │ └── release/ # Release配置
│ │
│ ├── build.gradle # 模块构建配置
│ └── proguard-rules.pro # 混淆规则
│
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
│
├── build.gradle # 项目构建配置
├── settings.gradle # 项目设置
├── gradle.properties # Gradle属性
├── local.properties # 本地配置(SDK路径)
└── capacitor.config.json # Capacitor配置副本
5.2 MainActivity.java #
java
package com.example.myapp;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
import com.getcapacitor.Plugin;
import java.util.ArrayList;
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
// 注册自定义插件
registerPlugin(MyCustomPlugin.class);
super.onCreate(savedInstanceState);
}
}
5.3 AndroidManifest.xml #
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<!-- 权限声明 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:name=".MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- 深度链接 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity>
</application>
</manifest>
5.4 build.gradle (app) #
groovy
apply plugin: 'com.android.application'
android {
namespace "com.example.myapp"
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.example.myapp"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
repositories {
flatDir {
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}
apply from: 'capacitor.build.gradle'
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch (Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. " + e.getMessage())
}
六、package.json配置 #
6.1 完整配置示例 #
json
{
"name": "my-app",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"cap:sync": "npx cap sync",
"cap:ios": "npx cap open ios",
"cap:android": "npx cap open android",
"cap:run:ios": "npx cap run ios",
"cap:run:android": "npx cap run android",
"build:ios": "npm run build && npx cap sync ios",
"build:android": "npm run build && npx cap sync android"
},
"dependencies": {
"@capacitor/android": "^6.0.0",
"@capacitor/core": "^6.0.0",
"@capacitor/ios": "^6.0.0",
"@capacitor/preferences": "^6.0.0",
"@capacitor/camera": "^6.0.0",
"@capacitor/geolocation": "^6.0.0",
"@capacitor/status-bar": "^6.0.0",
"@capacitor/splash-screen": "^6.0.0",
"@capacitor/keyboard": "^6.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@capacitor/cli": "^6.0.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.0",
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.0",
"typescript": "^5.0.2",
"vite": "^5.0.0"
}
}
七、最佳实践 #
7.1 目录组织建议 #
text
src/
├── app/ # 应用级配置
│ ├── App.tsx
│ └── router.tsx
│
├── features/ # 功能模块
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── services/
│ │ └── types/
│ └── todos/
│ ├── components/
│ ├── hooks/
│ └── types/
│
├── shared/ # 共享资源
│ ├── components/
│ ├── hooks/
│ ├── utils/
│ └── types/
│
├── native/ # 原生相关
│ ├── plugins/
│ ├── capacitor-setup.ts
│ └── permissions.ts
│
└── assets/ # 静态资源
├── images/
├── fonts/
└── icons/
7.2 环境配置 #
typescript
// src/config/env.ts
export const config = {
api: {
baseUrl: import.meta.env.VITE_API_URL || 'https://api.example.com'
},
app: {
name: import.meta.env.VITE_APP_NAME || 'My App'
}
};
7.3 Git忽略配置 #
gitignore
# Dependencies
node_modules/
# Build output
dist/
# iOS
ios/Pods/
ios/App/App.xcworkspace/xcuserdata/
*.ipa
*.dSYM.zip
# Android
android/.gradle/
android/app/build/
android/build/
*.apk
*.aab
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Environment
.env
.env.local
.env.*.local
八、总结 #
8.1 关键目录 #
| 目录 | 说明 |
|---|---|
| src/ | Web源码 |
| dist/ | Web构建输出 |
| ios/ | iOS原生项目 |
| android/ | Android原生项目 |
8.2 关键文件 #
| 文件 | 说明 |
|---|---|
| capacitor.config.json | Capacitor主配置 |
| package.json | npm配置 |
| ios/App/App/Info.plist | iOS应用配置 |
| android/app/src/main/AndroidManifest.xml | Android应用配置 |
8.3 下一步 #
了解项目结构后,让我们深入学习 运行时架构!
最后更新:2026-03-28