项目结构 #

一、整体结构 #

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