第一个应用 #
一、项目概述 #
我们将创建一个简单的待办事项应用,涵盖Capacitor开发的核心流程:
- 使用React + TypeScript构建Web应用
- 添加iOS和Android平台
- 使用Capacitor插件访问原生功能
- 在模拟器和真机上运行调试
二、创建项目 #
2.1 初始化项目 #
bash
# 创建Vite + React + TypeScript项目
npm create vite@latest todo-app -- --template react-ts
# 进入项目目录
cd todo-app
# 安装依赖
npm install
2.2 添加Capacitor #
bash
# 安装Capacitor核心包和CLI
npm install @capacitor/core @capacitor/cli
# 初始化Capacitor
npx cap init "Todo App" "com.example.todoapp"
# 按提示确认配置
# App name: Todo App
# App ID: com.example.todoapp
# Web dir: dist
2.3 项目结构 #
text
todo-app/
├── src/
│ ├── App.tsx
│ ├── App.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── public/
│ └── vite.svg
├── capacitor.config.json
├── package.json
├── tsconfig.json
└── vite.config.ts
三、开发Web应用 #
3.1 安装依赖 #
bash
# 安装UI库(可选)
npm install @ionic/react
npm install ionicons
3.2 创建应用代码 #
App.tsx #
tsx
import { useState } from 'react';
import './App.css';
interface Todo {
id: number;
text: string;
completed: boolean;
}
function App() {
const [todos, setTodos] = useState<Todo[]>([]);
const [inputValue, setInputValue] = useState('');
const addTodo = () => {
if (inputValue.trim()) {
setTodos([
...todos,
{
id: Date.now(),
text: inputValue.trim(),
completed: false
}
]);
setInputValue('');
}
};
const toggleTodo = (id: number) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
const deleteTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div className="app">
<header className="header">
<h1>Todo App</h1>
</header>
<div className="input-container">
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="添加新任务..."
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
/>
<button onClick={addTodo}>添加</button>
</div>
<ul className="todo-list">
{todos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</ul>
{todos.length === 0 && (
<p className="empty">暂无任务,添加一个吧!</p>
)}
</div>
);
}
export default App;
App.css #
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f5f5f5;
min-height: 100vh;
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
}
.app {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
padding-top: 20px;
}
.header h1 {
font-size: 28px;
color: #333;
}
.input-container {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.input-container input {
flex: 1;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 16px;
outline: none;
}
.input-container input:focus {
border-color: #4a90d9;
}
.input-container button {
padding: 12px 24px;
background-color: #4a90d9;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
}
.input-container button:active {
background-color: #357abd;
}
.todo-list {
list-style: none;
}
.todo-list li {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background: white;
border-radius: 8px;
margin-bottom: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.todo-list li.completed span {
text-decoration: line-through;
color: #999;
}
.todo-list li input[type="checkbox"] {
width: 20px;
height: 20px;
}
.todo-list li span {
flex: 1;
font-size: 16px;
}
.todo-list li button {
padding: 6px 12px;
background-color: #e74c3c;
color: white;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
}
.empty {
text-align: center;
color: #999;
margin-top: 40px;
}
3.3 配置Vite #
typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist',
sourcemap: true
},
server: {
host: true,
port: 5173
}
});
四、添加原生功能 #
4.1 安装插件 #
bash
# 安装存储插件
npm install @capacitor/preferences
# 安装键盘插件
npm install @capacitor/keyboard
# 安装状态栏插件
npm install @capacitor/status-bar
# 安装启动画面插件
npm install @capacitor/splash-screen
4.2 使用存储插件 #
更新 App.tsx:
tsx
import { useState, useEffect } from 'react';
import { Preferences } from '@capacitor/preferences';
import './App.css';
interface Todo {
id: number;
text: string;
completed: boolean;
}
const STORAGE_KEY = 'todos';
function App() {
const [todos, setTodos] = useState<Todo[]>([]);
const [inputValue, setInputValue] = useState('');
useEffect(() => {
loadTodos();
}, []);
useEffect(() => {
saveTodos();
}, [todos]);
const loadTodos = async () => {
try {
const { value } = await Preferences.get({ key: STORAGE_KEY });
if (value) {
setTodos(JSON.parse(value));
}
} catch (error) {
console.error('加载失败:', error);
}
};
const saveTodos = async () => {
try {
await Preferences.set({
key: STORAGE_KEY,
value: JSON.stringify(todos)
});
} catch (error) {
console.error('保存失败:', error);
}
};
const addTodo = () => {
if (inputValue.trim()) {
setTodos([
...todos,
{
id: Date.now(),
text: inputValue.trim(),
completed: false
}
]);
setInputValue('');
}
};
const toggleTodo = (id: number) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
const deleteTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div className="app">
<header className="header">
<h1>Todo App</h1>
</header>
<div className="input-container">
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="添加新任务..."
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
/>
<button onClick={addTodo}>添加</button>
</div>
<ul className="todo-list">
{todos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</ul>
{todos.length === 0 && (
<p className="empty">暂无任务,添加一个吧!</p>
)}
</div>
);
}
export default App;
4.3 配置状态栏和启动画面 #
创建 src/capacitor-setup.ts:
typescript
import { StatusBar, Style } from '@capacitor/status-bar';
import { SplashScreen } from '@capacitor/splash-screen';
import { Keyboard } from '@capacitor/keyboard';
import { Capacitor } from '@capacitor/core';
export const setupCapacitor = async () => {
if (Capacitor.isNativePlatform()) {
try {
await StatusBar.setStyle({ style: Style.Dark });
await StatusBar.setBackgroundColor({ color: '#4a90d9' });
await SplashScreen.hide({
fadeOutDuration: 500
});
Keyboard.addListener('keyboardWillShow', (info) => {
document.body.style.setProperty('--keyboard-height', `${info.keyboardHeight}px`);
});
Keyboard.addListener('keyboardWillHide', () => {
document.body.style.setProperty('--keyboard-height', '0px');
});
} catch (error) {
console.error('Capacitor setup error:', error);
}
}
};
在 main.tsx 中调用:
tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { setupCapacitor } from './capacitor-setup';
import './index.css';
setupCapacitor();
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
五、构建和同步 #
5.1 构建Web应用 #
bash
# 构建生产版本
npm run build
5.2 添加平台 #
bash
# 添加iOS平台
npx cap add ios
# 添加Android平台
npx cap add android
5.3 同步资源 #
bash
# 同步所有平台
npx cap sync
# 或单独同步
npx cap sync ios
npx cap sync android
六、iOS运行调试 #
6.1 打开Xcode #
bash
npx cap open ios
6.2 Xcode配置 #
-
选择签名团队
- 打开
Apptarget - 进入 Signing & Capabilities
- 选择你的开发者账号
- 打开
-
配置Info.plist(添加权限)
xml
<!-- ios/App/App/Info.plist -->
<key>NSCameraUsageDescription</key>
<string>需要访问相机来拍照</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册来选择照片</key>
6.3 运行应用 #
方式一:使用Xcode
- 选择模拟器或连接的设备
- 点击运行按钮
方式二:使用CLI
bash
# 在iOS模拟器运行
npx cap run ios
# 在指定设备运行
npx cap run ios --target "iPhone 14 Pro"
# 列出可用设备
npx cap run ios --list
6.4 调试技巧 #
bash
# 使用Safari开发者工具
# Safari → 开发 → 模拟器 → 网页
# 或使用外部开发服务器
# capacitor.config.json
{
"server": {
"url": "http://192.168.1.100:5173",
"iosScheme": "https"
}
}
七、Android运行调试 #
7.1 打开Android Studio #
bash
npx cap open android
7.2 Android Studio配置 #
-
同步Gradle
- 打开项目后自动提示同步
- 或点击 File → Sync Project with Gradle Files
-
配置签名(发布时需要)
- Build → Generate Signed Bundle/APK
7.3 运行应用 #
方式一:使用Android Studio
- 选择模拟器或连接的设备
- 点击运行按钮
方式二:使用CLI
bash
# 在Android模拟器运行
npx cap run android
# 在指定设备运行
npx cap run android --target <device-id>
# 列出可用设备
npx cap run android --list
7.4 调试技巧 #
bash
# 使用Chrome开发者工具
# chrome://inspect/#devices
# 或使用外部开发服务器
# capacitor.config.json
{
"server": {
"url": "http://192.168.1.100:5173",
"androidScheme": "https",
"cleartext": true
}
}
八、热重载开发 #
8.1 配置开发服务器 #
json
// capacitor.config.json
{
"appId": "com.example.todoapp",
"appName": "Todo App",
"webDir": "dist",
"server": {
"url": "http://192.168.1.100:5173",
"iosScheme": "https",
"androidScheme": "https",
"cleartext": true
}
}
8.2 开发流程 #
bash
# 终端1:启动开发服务器
npm run dev -- --host
# 终端2:同步并运行
npx cap sync
npx cap run ios
# 或
npx cap run android
8.3 注意事项 #
- 确保设备和开发机在同一网络
- 开发完成后记得移除
server.url配置 - 生产构建前需要重新构建Web应用
九、常见问题 #
9.1 iOS问题 #
问题:签名错误
text
解决:
1. 打开Xcode
2. 选择App target
3. Signing & Capabilities → 选择Team
问题:模拟器白屏
bash
# 清理并重新构建
rm -rf ios/App
npx cap add ios
npx cap sync ios
9.2 Android问题 #
问题:Gradle同步失败
bash
# 清理Gradle缓存
cd android
./gradlew clean
cd ..
npx cap sync android
问题:网络请求失败
xml
<!-- android/app/src/main/AndroidManifest.xml -->
<application
android:usesCleartextTraffic="true"
...>
十、项目优化 #
10.1 添加应用图标 #
iOS:
- 使用Xcode的资源目录
- 或使用
cordova-res工具
Android:
- 替换
android/app/src/main/res/下的图标文件 - 或使用Android Studio的Image Asset工具
10.2 添加启动画面 #
bash
# 安装cordova-res
npm install -g cordova-res
# 生成启动画面
cordova-res ios --skip-config --copy
cordova-res android --skip-config --copy
10.3 配置应用信息 #
iOS (Info.plist):
xml
<key>CFBundleDisplayName</key>
<string>Todo App</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
Android (strings.xml):
xml
<string name="app_name">Todo App</string>
<string name="title_activity_main">Todo App</string>
十一、总结 #
11.1 开发流程回顾 #
text
1. 创建项目 → npm create vite
2. 添加Capacitor → npm install @capacitor/core @capacitor/cli
3. 开发Web应用 → React/Vue/Angular
4. 构建Web应用 → npm run build
5. 添加平台 → npx cap add ios/android
6. 同步资源 → npx cap sync
7. 运行调试 → npx cap run ios/android
11.2 关键命令 #
| 命令 | 说明 |
|---|---|
npx cap init |
初始化Capacitor |
npx cap add |
添加平台 |
npx cap sync |
同步资源 |
npx cap open |
打开原生IDE |
npx cap run |
运行应用 |
11.3 下一步 #
现在你已经创建了第一个Capacitor应用,接下来让我们深入了解 项目结构!
最后更新:2026-03-28