
使用 Capacitor 构建混合应用:原生插件与 Web 转 App 迁移
Ionic 的 Capacitor 弥合了 Web 与原生移动开发之间的差距。它将 Web 应用封装在原生壳中,同时通过插件系统提供对原生设备功能的访问。本指南涵盖生产级 Capacitor 开发,从创建原生插件到迁移现有 Web 应用。
什么是 Capacitor?
Capacitor 是一个跨平台原生运行时,可在 iOS、Android 和 Web 上原生运行 Web 应用。与 Cordova 不同,Capacitor:
- 使用现代 Web 标准(ES 模块、async/await)
- 与现有原生工具(Xcode、Android Studio)集成
- 原生支持 Swift 和 Kotlin 插件
- 运行时占用空间小得多

设置 Capacitor
新项目设置
npm init @capacitor/app my-app
cd my-app
npm install @capacitor/core @capacitor/cli
npx cap init
# 添加平台
npx cap add ios
npx cap add android
现有 Web 应用集成
cd existing-web-app
npm install @capacitor/core @capacitor/cli
npx cap init "My App" com.example.myapp --web-dir dist
# 先构建 Web 应用
npm run build
# 同步到原生项目
npx cap sync
capacitor.config.ts
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'My App',
webDir: 'dist',
server: {
androidScheme: 'https',
// 开发时使用
// url: 'http://localhost:5173',
// cleartext: true,
},
plugins: {
SplashScreen: {
launchShowDuration: 2000,
launchAutoHide: true,
backgroundColor: '#ffffff',
iosSpinnerStyle: 'small',
showSpinner: false,
},
PushNotifications: {
presentationOptions: ['badge', 'sound', 'alert'],
},
LocalNotifications: {
smallIcon: 'ic_stat_icon_config_sample',
iconColor: '#488AFF',
},
},
ios: {
contentInset: 'automatic',
allowsLinkPreview: false,
},
android: {
allowMixedContent: false,
captureInput: true,
},
};
export default config;

使用官方 Capacitor 插件
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { Filesystem, Directory } from '@capacitor/filesystem';
import { Geolocation } from '@capacitor/geolocation';
import { PushNotifications } from '@capacitor/push-notifications';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
// 带回退的相机
async function capturePhoto(): Promise<string> {
const photo = await Camera.getPhoto({
resultType: CameraResultType.DataUrl,
source: CameraSource.Camera,
quality: 90,
allowEditing: false,
});
return photo.dataUrl ?? '';
}
// 文件系统操作
async function saveFile(filename: string, data: string): Promise<void> {
await Filesystem.writeFile({
path: filename,
data,
directory: Directory.Documents,
encoding: 'utf8',
});
}
// 地理定位
async function getCurrentPosition() {
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 10000,
});
return {
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy,
};
}
// 触觉反馈
async function triggerHaptic() {
await Haptics.impact({ style: ImpactStyle.Medium });
}
创建自定义原生插件
定义插件接口
// src/plugins/my-plugin/definitions.ts
export interface MyPluginPlugin {
echo(options: { value: string }): Promise<{ value: string }>;
getBatteryInfo(): Promise<BatteryInfo>;
watchBattery(callback: (info: BatteryInfo) => void): Promise<string>;
removeListener(listenerId: string): Promise<void>;
}
export interface BatteryInfo {
level: number;
isCharging: boolean;
temperature?: number;
}
iOS 实现(Swift)
// ios/App/Plugins/MyPlugin/MyPlugin.swift
import Foundation
import Capacitor
@objc(MyPlugin)
public class MyPlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "MyPlugin"
public let jsName = "MyPlugin"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "echo", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "getBatteryInfo", returnType: CAPPluginReturnPromise),
]
@objc func echo(_ call: CAPPluginCall) {
let value = call.getString("value") ?? ""
call.resolve(["value": value])
}
@objc func getBatteryInfo(_ call: CAPPluginCall) {
UIDevice.current.isBatteryMonitoringEnabled = true
let level = UIDevice.current.batteryLevel
let isCharging = UIDevice.current.batteryState == .charging
|| UIDevice.current.batteryState == .full
call.resolve([
"level": Int(level * 100),
"isCharging": isCharging,
])
}
}

Android 实现(Kotlin)
// android/app/src/main/java/com/example/app/MyPlugin.kt
package com.example.app
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin
import com.getcapacitor.JSObject
@CapacitorPlugin(name = "MyPlugin")
class MyPlugin : Plugin() {
@PluginMethod
fun echo(call: PluginCall) {
val value = call.getString("value") ?: ""
val ret = JSObject()
ret.put("value", value)
call.resolve(ret)
}
@PluginMethod
fun getBatteryInfo(call: PluginCall) {
val batteryIntent = context.registerReceiver(
null,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
val level = batteryIntent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
val scale = batteryIntent?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
val status = batteryIntent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1
val batteryPct = if (level != -1 && scale != -1) level * 100 / scale.toFloat() else -1f
val isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL
val ret = JSObject()
ret.put("level", batteryPct.toInt())
ret.put("isCharging", isCharging)
call.resolve(ret)
}
}
从 Cordova 迁移到 Capacitor
# 安装迁移工具
npm install -g @capacitor/cordova-migration
# 运行迁移
capacitor-cordova-migration migrate
手动步骤:
- 将 Cordova 插件替换为 Capacitor 等效插件
- 将
config.xml设置更新到capacitor.config.ts - 更新 JavaScript API 调用
- 测试每个原生功能
开发时热重载
// capacitor.config.ts - 开发配置
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'My App',
webDir: 'dist',
server: {
url: 'http://192.168.1.100:5173', // 你的本地 IP
cleartext: true,
},
};
# 启动开发服务器
npm run dev
# 在原生 IDE 中打开
npx cap open ios
npx cap open android
使用 Capacitor 的推送通知
import { PushNotifications } from '@capacitor/push-notifications';
async function registerPushNotifications() {
let permStatus = await PushNotifications.checkPermissions();
if (permStatus.receive === 'prompt') {
permStatus = await PushNotifications.requestPermissions();
}
if (permStatus.receive !== 'granted') {
throw new Error('推送通知权限被拒绝');
}
await PushNotifications.register();
}
// 监听注册令牌
PushNotifications.addListener('registration', (token) => {
console.log('推送注册成功:', token.value);
sendTokenToServer(token.value);
});
// 处理收到的推送通知
PushNotifications.addListener('pushNotificationReceived', (notification) => {
console.log('收到推送:', notification);
showInAppNotification(notification.title, notification.body);
});
// 处理点击通知
PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
const { data } = action.notification;
navigateToScreen(data.screen);
});
结论
Capacitor 在 Web 开发技能与原生移动交付之间提供了出色的桥梁。其插件架构使得访问原生 API 变得简单,同时保持 Web 应用作为主要代码库。对于拥有现有 Web 应用或强大 Web 专业知识的团队,Capacitor 提供了一条务实的跨平台移动应用路径,而无需牺牲原生能力。