用Capacitor打造跨平台应用的实战经验与踩坑总结

❤新霞 框架 阅读 744
赞 27 收藏
二维码
手机扫码查看
反馈

先跑起来再说,别纠结理论

我第一次接触 Capacitor 时,其实根本没搞懂它和 Cordova 有啥区别。但项目 deadline 压着,只能先动手。结果发现:真香。

用Capacitor打造跨平台应用的实战经验与踩坑总结

Capacitor 的核心优势就一点:用 Web 技术写原生 App,而且和现代前端框架(比如 Vue、React)集成得特别顺。不像 Cordova 那种老古董,还得手动维护一堆插件兼容性。我现在的项目基本都用 Capacitor 打包,iOS 和 Android 双端一套代码搞定,省下至少 30% 的联调时间。

下面直接上手一个最简流程——假设你已经有个 React 项目(Vue 也类似),怎么快速打包成 App。

npm install @capacitor/core @capacitor/cli
npx cap init

然后选 iOS 和 Android 平台:

npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add android

接着,把你的 Web 资源同步到原生工程里:

npx cap sync

这一步会自动生成 ios/android/ 目录。这时候你就可以用 Xcode 或 Android Studio 打开对应项目,直接运行了。亲测有效,连我这种对原生开发一窍不通的前端都能跑起来。

这个场景最好用:调用原生相机

Web 端调相机?在浏览器里可能行,但在 App 里就得靠原生能力。Capacitor 官方提供了 @capacitor/camera 插件,用起来比想象中简单。

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

const takePicture = async () => {
  const image = await Camera.getPhoto({
    quality: 90,
    allowEditing: true,
    resultType: CameraResultType.Uri
  });
  // image.webPath 可以直接用于 <img src={image.webPath} />
  console.log('图片路径:', image.webPath);
};

注意:iOS 需要在 Info.plist 里加权限描述,不然直接 crash。Android 也要在 AndroidManifest.xml 里声明权限。这些 Capacitor 都会在 add 平台时自动加好,但如果你升级了插件,最好检查一下有没有漏掉。

另外,resultType 别用 Base64,除非你真需要。大图转 Base64 内存爆炸,我踩过坑,App 直接卡死。用 Uri 最稳妥,Web 层直接当 URL 用。

踩坑提醒:这三点一定注意

Capacitor 虽然好用,但有几个坑我反复踩过,必须提醒你:

  • 本地开发服务器地址问题:默认情况下,Capacitor App 加载的是 public/ 下的静态文件。但如果你用了 dev server(比如 Vite 的 http://localhost:5173),在真机上是访问不到的。解决办法是:要么 build 出静态文件再 sync,要么用 capacitor.config.ts 配置 server.url 指向你的内网 IP(比如 http://192.168.1.100:5173)。但后者只适合开发调试,发布前记得切回来。
  • iOS 状态栏遮挡内容:iPhone 的刘海屏会让页面顶部被盖住。Capacitor 提供了 @capacitor/status-bar 插件,但更简单的做法是用 CSS 安全区域:
body {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

这个一行 CSS 解决 90% 的状态栏问题,比 JS 动态计算靠谱多了。

  • Android 返回键行为:默认按返回键会直接退出 App。如果你用了路由(比如 React Router),可能希望先退回上一页。这时候得监听 backbutton 事件:
import { App } from '@capacitor/app';

App.addListener('backButton', ({ canGoBack }) => {
  if (!canGoBack) {
    // 这里可以弹出“再按一次退出”提示
    App.exitApp();
  } else {
    window.history.back();
  }
});

但注意:canGoBack 在 Capacitor 里其实是假的,它永远返回 false。所以实际判断得靠你自己维护路由栈。我现在的做法是用一个全局变量记录当前是否在首页,不是首页就 history.back(),是首页就退出。糙但有效。

高级技巧:自定义插件打通原生能力

官方插件覆盖不了所有需求?比如我想调用 iOS 的 Face ID。这时候就得写自定义插件。

以 iOS 为例,在 ios/App/ 目录下新建一个 Swift 文件,比如 BiometricAuthPlugin.swift

import Foundation
import Capacitor
import LocalAuthentication

@objc(BiometricAuthPlugin)
public class BiometricAuthPlugin: CAPPlugin {
    @objc func authenticate(_ call: CAPPluginCall) {
        let context = LAContext()
        var error: NSError?

        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "验证身份") { success, _ in
                DispatchQueue.main.async {
                    if success {
                        call.resolve(["authenticated": true])
                    } else {
                        call.reject("认证失败")
                    }
                }
            }
        } else {
            call.reject("设备不支持生物识别")
        }
    }
}

然后在 capacitor.config.ts 里注册:

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

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'MyApp',
  webDir: 'dist',
  plugins: {
    BiometricAuthPlugin: {
      // 配置项
    }
  }
};

export default config;

JS 层调用就很简单:

import { Plugins } from '@capacitor/core';
const { BiometricAuthPlugin } = Plugins;

try {
  await BiometricAuthPlugin.authenticate();
  console.log('验证成功');
} catch (e) {
  console.error('验证失败:', e.message);
}

虽然写原生代码有点劝退,但一旦打通,后续调用就和 Web API 一样简单。我建议:能用官方插件就用,实在不行再自己写。毕竟维护自定义插件也是成本。

别忘了网络请求的坑

在 App 里发请求,很多人直接用 fetch('https://jztheme.com/api/data')。但 iOS 默认禁止 HTTP(非 HTTPS)请求,Android 10+ 也有类似限制。所以:

  • 确保你的 API 是 HTTPS
  • 如果测试环境只有 HTTP,iOS 需要在 Info.plistNSAppTransportSecurity 白名单(仅限开发!)
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

但上线前一定要删掉这个配置,否则审核被拒。我之前就因为忘了删,被 Apple 打回来两次,折腾半天。

最后说两句

Capacitor 不是银弹,但它确实让前端开发者能低成本地触达原生能力。我的经验是:80% 的需求用官方插件就能解决,剩下 20% 要么绕过去,要么写个简单插件。

以上是我踩坑后的总结,希望对你有帮助。这个技术的拓展用法还有很多(比如和 Firebase 集成、后台任务处理),后续会继续分享这类博客。有更优的实现方式欢迎评论区交流——毕竟谁还没被原生平台坑过呢?

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论