跳到主要内容
版本:v8

在你的应用中更新 Capacitor 至 3.0

Capacitor 3 为生态系统带来了关键的更新和激动人心的新功能。

阅读 Capacitor 3.0 公告 ›

将你的应用升级到 Capacitor 3 后,是否愿意在这个讨论中分享你的反馈?我们很希望听到你的意见!💖

如果你是插件作者,希望将插件升级到更新的 Capacitor 版本,请参阅 Capacitor 插件升级指南

NodeJS 12+

Node 8 已结束生命周期。Node 10 将于 2021 年 4 月 30 日结束生命周期。Capacitor 3 需要 NodeJS 12 或更高版本。(推荐使用最新的 LTS 版本。)

Ionic CLI

如果你使用 Ionic CLI,对 Capacitor 3 的官方支持始于版本 6.16.0。我们建议此时通过 npm install -g @ionic/cli 升级到最新版本。

更新 Capacitor CLI 和 Core

npm install @capacitor/cli@latest-3 @capacitor/core@latest-3

ES2017+

Capacitor 3 现在针对 ES2017 环境构建,而非 ES5。插件模板也已更新为针对 ES2017,建议第三方插件更新其目标。

此更改不应影响你的应用,除非你支持 IE11(Capacitor 不官方支持 IE11)。

TypeScript 3.8+

Capacitor 3 使用更新的 TypeScript 语法,该语法只能在 TS 3.8 或更高版本中使用。

Capacitor 配置更改

如果你已安装 TypeScript 3.8+,可以将 capacitor.config.json 迁移为名为 capacitor.config.ts 的带类型的 TypeScript 配置文件。你可以继续使用 .json 文件,但 TypeScript 配置文件可能为你的团队提供更好的开发体验。以下是 Capacitor 测试应用 中使用的 capacitor.config.ts 文件示例。

/// <reference types="@capacitor/local-notifications" />
/// <reference types="@capacitor/push-notifications" />
/// <reference types="@capacitor/splash-screen" />

import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
appId: 'com.capacitorjs.app.testapp',
appName: 'capacitor-testapp',
webDir: 'build',
plugins: {
SplashScreen: {
launchAutoHide: false,
},
LocalNotifications: {
smallIcon: 'ic_stat_icon_config_sample',
iconColor: '#CE0B7C',
},
PushNotifications: {
presentationOptions: ['alert', 'sound'],
},
},
};

export default config;

官方插件

所有插件已从 Capacitor 核心中移除,并放入各自的 npm 包中。这样做有几个原因(参见 #3227),核心团队确信这是正确的方向。你可以像这样导入核心插件:

import { Camera } from '@capacitor/camera';

已移除的插件:Background Task、Permissions 和 Photos

  • Background Task:此插件似乎很少使用,并且没有达到大多数开发者预期的效果。核心团队将在未来重新处理后台功能。订阅 #3032 以获取更新。
  • Permissions:核心团队已实现了一种替代这种集中式方法的方式,社区插件也可以采用(参见新的 Permissions API)。
  • Photos:这个没有文档记录的 iOS 专用插件已被移除。请使用 @capacitor-community/media

拆分后的插件:Accessibility、App 和 Modals

  • Accessibility
  • App
    • 应用相关信息和功能保留在 App
    • 应用 URL 处理(openUrl()canOpenUrl())已移入 App Launcher
  • Modals
    • Action Sheet 功能(showActions())已移入 Action Sheet
    • 对话框功能(alert()prompt()confirm())已移入 Dialog

迁移你的应用以使用新的官方插件包

此更改将要求你单独安装每个你正在使用的插件。

  1. 搜索你的项目中从 @capacitor/corePlugins 对象中提取的核心插件
  2. 找到对应的插件文档,记住一些插件已被拆分
  3. 按照文档中每个插件的安装说明进行操作
  4. 将插件导入改为从插件包导入(参见插件导入
  5. 遵循向后不兼容的插件更改中的说明

使用 Ionic Framework?

Ionic Framework 使用了以下插件中的 API:

为了获得最佳的 Ionic Framework 用户体验,即使你没有在应用中导入它们,也应确保安装这些插件:

npm install @capacitor/app @capacitor/haptics @capacitor/keyboard @capacitor/status-bar

插件导入

Plugins 对象已被弃用,但在 Capacitor 3 中仍将继续工作。Capacitor 插件应更新为使用新的插件注册 API(参见插件升级指南),这将允许直接从插件包中导入它们。

今后,不应使用 @capacitor/core 中的 Plugins 对象。

// 旧方式
import { Plugins } from '@capacitor/core';
const { AnyPlugin } = Plugins;

推荐直接从插件包中导入插件,但插件必须更新为与 Capacitor 3 兼容才能实现。

// 新方式
import { AnyPlugin } from 'any-plugin';

向后不兼容的插件更改

虽然许多插件 API 保持不变以简化向 Capacitor 3 的迁移过程,但有些仍需要代码更新和手动迁移。

  • Accessibility / Screen Reader
    • isScreenReaderEnabled() 方法已重命名为 isEnabled()
    • 'accessibilityScreenReaderStateChange' 事件已重命名为 'stateChange'
    • 在 Android 和 iOS 上,speak() 仅当屏幕阅读器当前处于活动状态时才能工作。如需在屏幕阅读器活动或不活动时都能使用文字转语音功能,请使用 @capacitor-community/text-to-speech
  • Browser
    • prefetch() 已被移除。
  • Device
    • 应用信息已从 getInfo() 中移除(appVersionappBuildappIdappName)。请使用 App 插件的 getInfo() 获取此信息。
    • uuid 已从 getInfo() 中移除。请使用新的 getId() 函数。
  • Haptics
    • HapticsNotificationType 枚举的键已从大写改为驼峰命名,以与其他枚举保持一致。
  • Local Notifications
    • 此插件现在使用新的 Permissions API。requestPermission() 已被移除,请使用 requestPermissions()
  • Push Notifications
    • 此插件现在使用新的 Permissions API。requestPermission() 已被移除,请使用 requestPermissions()
  • Share
    • share() 方法现在返回 ShareResult 而不是 any
    • share() 的返回值不再包含 completed。如果未完成,将直接拒绝(reject)而不是返回。
  • Storage
    • 需要数据迁移! 内部存储机制已更改,需要数据迁移。已添加一个便利方法:migrate()。要在不影响最终用户的情况下更新你的应用,请在任何其他方法之前调用 migrate()
  • Filesystem
    • stat() 方法现在在所有平台上以毫秒为单位返回 ctime 和 mtime 时间戳。以前,iOS 以秒为单位返回时间戳。

日志记录更改

hideLogs 配置选项在 Capacitor 3 中已被弃用。它已被新的 loggingBehavior 配置选项替代。详细信息可以在配置文档中找到。

iOS

Capacitor 3 支持 iOS 12+。需要 Xcode 12+。建议使用 CocoaPods 1.8+。

更新 CocoaPods

建议将 CocoaPods 升级到最新的稳定版本。CocoaPods 1.8 切换到使用 CDN,这意味着不再需要定期运行 pod repo update

使用 pod --version 检查你的 CocoaPods 版本,并访问 cocoapods.org 获取安装说明。

将 iOS 部署目标设置为 12.0

对你的 Xcode 项目和应用 target 执行以下操作:打开 Build Settings 选项卡。在 Deployment 部分,将 iOS Deployment Target 改为 iOS 12.0

然后,打开 ios/App/Podfile 并将 iOS 版本更新为 12.0:

-platform :ios, '11.0'
+platform :ios, '12.0'
use_frameworks!

将 Swift 版本设置为 5

如果你的应用尚未使用 Swift 5,请打开 Xcode target 的 Build Settings 选项卡,然后在 Swift Compiler - Language 部分将 Swift Language Version 改为 Swift 5

public 移入 iOS target 目录

在 Capacitor 3 中,建议将 ios/App/public 目录移入 ios/App/App/public。这可以通过 Xcode 实现:

移除现有的 public 文件夹

  1. 展开 App 项目下的文件树,然后展开 App 组,选择 public 文件夹。
  2. 右键单击 Delete。当提示是删除文件夹还是仅移除引用时,选择 Move to Trash

删除 public 文件夹

在新位置重新创建 public

  1. 右键单击 App 项目中的 App 组,然后单击 Add Files to "App"...
  2. 保留默认选项(确保创建文件夹引用而非组,并添加到 App target)。
  3. 单击 New Folder,命名为 "public"。
  4. 单击 Create,然后单击 Add

重新创建 public 文件夹

在 Xcode 中看起来可能一样,但新的 public 文件夹现在应相对于 App 组,而不是项目根目录。

gitignore 新的 public 文件夹

ios/.gitignore 中,将忽略路径从 App/public 改为 App/App/public。此文件夹包含你的 Web 资源的副本,不应提交。

 App/build
App/Pods
-App/public
+App/App/public
App/Podfile.lock
xcuserdata

更新 Capacitor iOS 平台

npm install @capacitor/ios@latest-3
npx cap sync ios

在应用事件中从 CAPBridge 切换到 ApplicationDelegateProxy

ios/App/App/AppDelegate.swift 中,进行以下更新:

     func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// 当应用通过 URL 启动时调用。请随意在此添加额外处理,
// 但如果你希望 App API 支持跟踪应用 URL 打开,请确保保留此调用
- return CAPBridge.handleOpenUrl(url, options)
+ return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// 当应用通过活动启动时调用,包括 Universal Links。
// 请随意在此添加额外处理,但如果你希望 App API 支持
// 跟踪应用 URL 打开,请确保保留此调用
- return CAPBridge.handleContinueActivity(userActivity, restorationHandler)
+ return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
}

移除 USE_PUSH 编译条件

如果使用推送通知功能,在 ios/App/App/AppDelegate.swift 中,进行以下更新:


- #if USE_PUSH

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: error)
}

-#endif

如果不使用推送通知,你可以移除整个代码块:

-    #if USE_PUSH
-
- func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
- NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: deviceToken)
- }
-
- func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
- NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: error)
- }
-
-#endif

从硬编码的 CAPNotifications 切换到 NSNotification 扩展

ios/App/App/AppDelegate.swift 中,进行以下更新:

     override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)

let statusBarRect = UIApplication.shared.statusBarFrame
guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }

if statusBarRect.contains(touchPoint) {
- NotificationCenter.default.post(CAPBridge.statusBarTappedNotification)
+ NotificationCenter.default.post(name: .capacitorStatusBarTapped, object: nil)
}
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
- NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: deviceToken)
+ NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
- NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: error)
+ NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
}

忽略 DerivedData

DerivedData 添加到 ios/.gitignore 文件中。这是 Capacitor CLI 放置原生 iOS 构建的位置。

 App/Pods
App/App/public
App/Podfile.lock
+DerivedData
xcuserdata

# Capacitor 的 Cordova 插件

Android

Capacitor 3 支持 Android 5+(并新增支持 Android 11)。需要 Android Studio 4+。

更新 Capacitor Android 平台

npm install @capacitor/android@latest-3
npx cap sync android

切换到自动 Android 插件加载

在 Capacitor 3 中,建议自动加载 Android 插件。在 MainActivity.java 中,可以移除 onCreate 方法。在添加或移除通过 npm 安装的插件时,你不再需要编辑此文件。

 public class MainActivity extends BridgeActivity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // 初始化 Bridge
- this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
- // 你安装的额外插件放在这里
- add(Plugin1.class);
- add(Plugin2.class);
- }});
- }
}

如果你的应用包含专门为你的应用程序构建的自定义插件,你仍然需要在 onCreate 中注册插件:

 public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

+ registerPlugin(PluginInMyApp.class);
}
}

更新 Gradle 至 7.0

我们现在建议在 Capacitor 项目中使用 Gradle 7.0。在 Android Studio 中,打开 File 菜单,然后单击 Project Structure。在 Project 部分,将 Gradle Version 改为 7.0,将 Android Gradle Plugin Version 改为 4.2.0。然后单击 OK

你可能希望在 Project Structure 对话框的 Suggestions 部分评估对 Android 包的建议更新。

更新 Android 变量

android/variables.gradle 中,你可以更新以下变量:

 ext {
minSdkVersion = 21
- compileSdkVersion = 29
- targetSdkVersion = 29
+ compileSdkVersion = 30
+ targetSdkVersion = 30
+ androidxActivityVersion = '1.2.0'
- androidxAppCompatVersion = '1.1.0'
+ androidxAppCompatVersion = '1.2.0'
+ androidxCoordinatorLayoutVersion = '1.1.0'
- androidxCoreVersion = '1.2.0'
- androidxMaterialVersion = '1.1.0-rc02'
- androidxBrowserVersion = '1.2.0'
- androidxLocalbroadcastmanagerVersion = '1.0.0'
- androidxExifInterfaceVersion = '1.2.0'
- firebaseMessagingVersion = '20.1.2'
- playServicesLocationVersion = '17.0.0'
+ androidxCoreVersion = '1.3.2'
+ androidxFragmentVersion = '1.3.0'
- junitVersion = '4.12'
- androidxJunitVersion = '1.1.1'
- androidxEspressoCoreVersion = '3.2.0'
+ junitVersion = '4.13.1'
+ androidxJunitVersion = '1.1.2'
+ androidxEspressoCoreVersion = '3.3.0'
cordovaAndroidVersion = '7.0.0'
}

Capacitor 3 支持 Android 11(API 30),因此你可以将 SDK target 更新为 30。将 compileSdkVersiontargetSdkVersion 改为 30

新增了 androidxActivityVersion 变量,将其值设置为 1.2.0

androidxAppCompatVersion 可以更新为 1.2.0

新增了 androidxCoordinatorLayoutVersion 变量,将其值设置为 1.1.0

androidxCoreVersion 可以更新为 1.3.2

androidxMaterialVersion 变量被 Action Sheet 和 Camera 插件使用,如果不使用它们可以移除。如果使用,请查看 Camera 文档Action Sheet 文档

androidxBrowserVersion 变量被 Browser 插件使用,如果不使用该插件可以移除。如果使用,请查看文档

androidxLocalbroadcastmanagerVersion 变量可以移除。

androidxExifInterfaceVersion 变量被 Camera 插件使用,如果不使用该插件可以移除。如果使用,请查看文档

firebaseMessagingVersion 变量被 Push Notifications 插件使用,如果不使用该插件可以移除。如果使用,请查看文档

playServicesLocationVersion 变量被 Geolocation 插件使用,如果不使用该插件可以移除。如果使用,请查看文档

新增了 androidxFragmentVersion 变量,将其值设置为 1.3.0

junitVersion 可以更新为 4.13.1

androidxJunitVersion 可以更新为 1.1.2

androidxEspressoCoreVersion 可以更新为 3.3.0

移除未使用和冗余的权限

根据你使用的插件,你可以选择从应用的 AndroidManifest.xml 文件中移除未使用的权限。新 Capacitor 应用的清单文件 仅包含 INTERNET,因为权限现在应在安装插件时添加。按照以下步骤移除未使用的权限:

  1. 确定你的应用使用的插件
  2. 阅读本文档中每个插件的安装说明,查找每个插件所需的权限
  3. 在你的应用的 AndroidManifest.xml 文件中,保留你的插件所需的权限,移除未使用的权限

Haptics 和 Network 插件就是这样的例子,它们现在在自己各自的 AndroidManifest.xml 文件中包含了安装时所需的权限,这些权限最终会合并到你的应用中。从你的应用的 AndroidManifest.xml 文件中安全地移除它们的权限是安全的:

     <!-- 权限 -->

<uses-permission android:name="android.permission.INTERNET" />

- <!-- Network API -->
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

- <!-- Vibration API -->
- <uses-permission android:name="android.permission.VIBRATE" />

</manifest>